This commit is contained in:
2024-10-09 16:13:22 +00:00
commit 0ea3f048dc
437 changed files with 44406 additions and 0 deletions

40
data_generation/README.md Executable file
View File

@@ -0,0 +1,40 @@
# Update: Get View Interface
* First, generate a scene file by
```sh
# Remember to specify dataset path in the script's main function
python data_generation/tools/generate_scene_pcd.py
# It will generate a scene.pickle file in the dataset folder
```
* Then, try get_view function
```sh
# Remember to specity the scene.pickle file path and camera pose in the script's main function
python data_generation/tools/get_view.py
# The get_view() function will return scene_pcl and obj_pcl_dict
```
# Data Generation
This folder contains assets related to generating datasets. Maintained by Zhengxiao Han.
## 1 Prerequisites
* **OmniObject3d-simplified** dataset downloaded. Contact Jiyao to acquire it.
* [Isaac Sim](https://docs.omniverse.nvidia.com/isaacsim/latest/index.html) installed.
## 2 How to Use
### 2.1 Convert Dataset to .usd Format
```sh
~/.local/share/ov/pkg/isaac_sim-2023.1.1/python.sh <path-to-this-repo>/data_generation/tools/convert_dataset.py <path-to-the-dataset>
# For example:
# ~/.local/share/ov/pkg/isaac_sim-2023.1.1/python.sh /home/hzx/Projects/ActivePerception/data_generation/tools/convert_dataset.py /home/hzx/Downloads/OmniObject3d-simplified/output
```
### 2.2 Run the Script
* Firstly Specify file paths in [data_generation.yaml](https://github.com/Jiyao06/ActivePerception/blob/main/data_generation/config/data_generation/data_generation.yaml) and [replicator.yaml](https://github.com/Jiyao06/ActivePerception/blob/main/data_generation/config/replicator/replicator.yaml)
* Then run this script:
```sh
~/.local/share/ov/pkg/isaac_sim-2023.1.1/python.sh <path-to-this-repo>/data_generation/src/generate_objects.py
```

View File

@@ -0,0 +1,29 @@
background_path: "/home/hzx/Projects/ActivePerception/data_generation/data/backgrounds/env.usd"
object_dataset_path: "/home/hzx/Projects/ActivePerception/data_generation/data/objects"
occluder_dataset_path: "/home/hzx/Projects/ActivePerception/data_generation/data/occluders/table/table_001"
data_save_path: "/home/hzx/Projects/ActivePerception/data_generation/output"
# Scene Generation Params
num_scenes: 10
# Occluder Spawning Params
distance_threshold: 0.1
# Objects Spawning Params
model_scaling : [0.001, 0.001, 0.001]
object_num: 20
max_spawn_z: 2.0
spawn_z_offset: 0.025
num_attemp_times: 50
max_num_planes: 2
# Camera Sampling Params
min_radius: 1.4
max_radius: 1.5
hfov: 90.0
start_h_angle: 0.0
dr: 0.05
dhfov: 30.0
dvfov: 30.0
look_at_position: [0, 0, 0.3]
look_at_noise: 0.075

View File

@@ -0,0 +1,8 @@
width: 1920
height: 1080
sync_loads: True
headless: False
renderer: "RayTracedLighting"
physics_dt: 0.01 #100hz
rendering_dt: 0.01 #100hz
meters_unit: 1.0

View File

@@ -0,0 +1,16 @@
writer: "BasicWriter"
writer_config:
output_dir: "/home/hzx/Projects/ActivePerception/data_generation/output"
rgb: True
camera_params: True
bounding_box_2d_tight: False
semantic_segmentation: True
distance_to_image_plane: True
distance_to_camera: False
bounding_box_3d: False
occlusion: False
resolution: [640, 480]
max_sim_steps: 50
rt_subframes: 1
num_frames: 1

View File

@@ -0,0 +1,74 @@
simple_room:
table_001:
env: 0.255
obj: -0.21
table_002:
env: 0.26
obj: -0.22
table_003:
env: 0.265
obj: -0.167
table_004:
env: 0.225
obj: -0.19
table_005:
env: 0.14
obj: -0.315
table_007:
env: 0.53
obj: -0.095
table_008:
env: 0.215
obj: -0.255
table_009:
env: 0.245
obj: -0.2
table_013:
env: 0.51
obj: -0.115
table_014:
env: 0.325
obj: -0.2
table_015:
env: 0.29
obj: -0.215
table_016:
env: 0.23
obj: -0.255
table_017:
env: 0.32
obj: -0.145
table_018:
env: 0.17
obj: -0.128
table_019:
env: 0.26
obj: -0.22
table_020:
env: 0.01
obj: -0.31
table_021:
env: 0.295
obj: -0.235
table_022:
env: 0.05
obj: -0.055
table_023:
env: 0.046
obj: -0.055
table_024:
env: 0.015
obj: -0.255
table_025:
env: 0.29
obj: -0.26
table_026:
env: 0.03
obj: -0.3
table_027:
env: 0.01
obj: -0.31
table_026:
env: 0.41
obj: -0.08

View File

@@ -0,0 +1,435 @@
import sys
import yaml
import omni
from omni.isaac.kit import SimulationApp
import carb
from torch._C import NoneType
import os
import time
import random
import numpy as np
import trimesh
from sklearn import linear_model
import copy
import math
from generate_objects_utils import *
class GENERATE_BOJECTS():
def __init__(self):
# Initialize Parameters
self._read_params()
# Initialize Isaac Sim Simulator
self._setup_isaac_sim()
# Initialize Variables and Parameters Related to Data Generation
self._setup_data_generatin_params()
# Start Main Function
self._main()
def _read_params(self):
# Load Parameters for Launching Isaac Sim
_isaac_sim_config_path = sys.path[0]+'/../config/isaac_sim/isaac_sim.yaml'
with open(_isaac_sim_config_path, 'r') as config:
self._isaac_sim_config = yaml.safe_load(config)
# Load Parameters for Data Generation
_data_generation_config_path = sys.path[0]+'/../config/data_generation/data_generation.yaml'
with open(_data_generation_config_path, 'r') as config:
self._data_generation_config = yaml.safe_load(config)
if(self._data_generation_config["data_save_path"][-1]=="/"):
self._data_generation_config["data_save_path"][-1] = self._data_generation_config["data_save_path"][:-1]
# Load Parameters for Replicator
_replicator_config_path = sys.path[0]+'/../config/replicator/replicator.yaml'
with open(_replicator_config_path, 'r') as config:
self._replicator_config = yaml.safe_load(config)
if(self._replicator_config["writer_config"]["output_dir"][-1]=="/"):
self._replicator_config["writer_config"]["output_dir"] = self._replicator_config["writer_config"]["output_dir"][:-1]
# Calculate Parameters
self._num_r = int((self._data_generation_config["max_radius"]-self._data_generation_config["min_radius"])/self._data_generation_config["dr"])
# carb.log_error("num_r: "+str(self._data_generation_config["max_radius"]-self._data_generation_config["min_radius"]))
self._num_h = int(self._data_generation_config["hfov"]/self._data_generation_config["dhfov"])
self._num_v = int(360/self._data_generation_config["dvfov"])
def _setup_isaac_sim(self):
# Start the omniverse application
self._simulation_app = SimulationApp(launch_config=self._isaac_sim_config)
# The following items must be imported after simulation is initialized
from omni.isaac.core import World
from omni.isaac.dynamic_control import _dynamic_control
# Acquire dynamic control interface
self._dc = _dynamic_control.acquire_dynamic_control_interface()
# Disable joystick to avoid conflict
carb.settings.get_settings().set("persistent/app/omniverse/gamepadCameraControl", False)
# Create a new world
self._world = World(physics_dt=self._isaac_sim_config["physics_dt"],
rendering_dt=self._isaac_sim_config["rendering_dt"],
stage_units_in_meters=self._isaac_sim_config["meters_unit"])
if(self._world is NoneType):
carb.log_error("Failed to set world.")
self._stage = omni.usd.get_context().get_stage()
self._simulation_app.update()
# Start Simulation
omni.timeline.get_timeline_interface().play()
def _setup_replicator(self, scene_id = 0):
# Initialize Replicator
import omni.replicator.core as rep
self._rep = rep
# Setup Cameras
self._driver_cam = self._rep.create.camera(name="DriverCam")
self._driver_rp = self._rep.create.render_product(self._driver_cam, self._replicator_config["resolution"], name="DriverView")
# Setup Writers
output_path = self._replicator_config["writer_config"]["output_dir"]
self._replicator_config["writer_config"]["output_dir"] = output_path + "/scene_" + str(scene_id)
self._writer = self._rep.WriterRegistry.get(self._replicator_config["writer"])
self._writer.initialize(**self._replicator_config["writer_config"])
self._writer.attach(self._driver_rp)
def _setup_data_generatin_params(self):
self._trimesh_objects = []
self._trimesh_occluders = []
self._occluder_top_z = 0
self._occluder_spawn_x = 0
self._occluder_spawn_y = 0
self._occluder_spawn_z = 0
self._occluder_x_lb = 0
self._occluder_x_ub = 0
self._occluder_y_lb = 0
self._occluder_y_ub = 0
self._selected_occluder_index = 0
self._selected_object_indices = []
self._replicator_objects = []
def _extract_model_type(self, path):
# Specify it according to specific file directory
NUM_SLASH_BEFORE_TYPE = 2
num_slash = 0
object_type_str = []
for i in range(len(path)):
index = len(path) -1 - i
char = path[index]
if(num_slash == NUM_SLASH_BEFORE_TYPE):
object_type_str.append(char)
if(char == "/"):
num_slash += 1
object_type_str.reverse()
object_type_str = ''.join(object_type_str[1:])
return object_type_str
def _extract_model_name(self, path):
# Specify it according to specific file directory
NUM_SLASH_BEFORE_NAME = 1
num_slash = 0
object_name_str = []
for i in range(len(path)):
index = len(path) -1 - i
char = path[index]
if(num_slash == NUM_SLASH_BEFORE_NAME):
object_name_str.append(char)
if(char == "/"):
num_slash += 1
object_name_str.reverse()
object_name_str = ''.join(object_name_str[1:])
return object_name_str
def _add_background(self, scene_usd_path):
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.core.prims import XFormPrim
add_reference_to_stage(usd_path=scene_usd_path, prim_path="/World/Env")
self._world.scene.add(XFormPrim(prim_path="/World/Env", name=scene_usd_path,
position=[0,0,0], orientation=[0,0,0,1], scale=[1,1,1]))
def _set_background_position(self, position):
_object_prim = self._stage.GetPrimAtPath("/World/Env")
_object_prim.GetAttribute("xformOp:translate").Set((position[0], position[1], position[2]))
def _delete_background(self):
import omni.isaac.core.utils.prims as prims_utils
prims_utils.delete_prim("/World/Env")
def _add_object_to_sim(self, obj_index):
import omni.replicator.core as rep
# Load Object
trimesh_obj = self._trimesh_objects[obj_index]
# Calculate proper spawn pose
bounding_box = trimesh_obj.bb
# spawn_z = abs(0-bounding_box[0][1]) + self._occluder_top_z + 0.01
convex_hulls = self._trimesh_occluders[self._selected_occluder_index].convex_hulls
num_planes = len(convex_hulls)
if(num_planes >= self._data_generation_config["max_num_planes"]):
num_planes = self._data_generation_config["max_num_planes"]
random_plane_index = random.randint(0, num_planes-1)
convex_hull = convex_hulls[random_plane_index]
# calculate spawn height
plane_z = convex_hull.get_average_z()
spawn_z = self._occluder_spawn_z + plane_z + -bounding_box[0][2] + self._data_generation_config["spawn_z_offset"]
if(spawn_z > self._data_generation_config["max_spawn_z"]):
# spawn_z = self._data_generation_config["max_spawn_z"]
return False
# carb.log_error("Spawning Object: "+ trimesh_obj.name+", z: "+str(spawn_z))
# get spawning plane bounding box
convex_hull_bounds = convex_hull.get_convex_hull()
convex_hull_x_bounds = np.asarray(convex_hull_bounds).T[0]
convex_hull_y_bounds = np.asarray(convex_hull_bounds).T[1]
convex_min_x = min(convex_hull_x_bounds)
convex_max_x = max(convex_hull_x_bounds)
convex_min_y = min(convex_hull_y_bounds)
convex_max_y = max(convex_hull_y_bounds)
# get object bounding box
bb_origin = [(bounding_box[0][0]+bounding_box[1][0])/2, (bounding_box[0][0]+bounding_box[1][0])/2]
bb_width = abs(bounding_box[0][0]-bounding_box[1][0])
bb_height = abs(bounding_box[0][1]-bounding_box[1][1])
# carb.log_error("origin: "+str(bb_origin)+", width: "+str(bb_width)+", height: "+str(bb_height))
for i in range(self._data_generation_config["num_attemp_times"]):
random_origin_x = random.uniform(convex_min_x, convex_max_x)
random_origin_y = random.uniform(convex_min_y, convex_max_y)
bb_origin[0] += random_origin_x
bb_origin[1] += random_origin_y
rot_z = random.uniform(-np.pi,np.pi)
bb = BOUNDING_BOX_2D(bb_origin, bb_width, bb_height, rot_z)
if(convex_hull.are_points_inside_convex_hull(bb._get_bb_vertices())):
# carb.log_error("i: "+str(i)+", bb: "+str(bb._get_bb_vertices())+", convex_hull: "+str(convex_hull_bounds[:3]) )
place_x = bb_origin[0]
place_y = bb_origin[1]
# Spawn Object
object = rep.create.from_usd(trimesh_obj.usd_path, semantics=[("class", trimesh_obj.name)])
with object:
rep.modify.pose(position=(place_x, place_y, spawn_z), rotation_x = -180, rotation_z = -90, scale=self._data_generation_config["model_scaling"])
self._replicator_objects.append(object)
return True
return False
def _add_occluder_to_sim(self, obj_index):
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.core.prims import XFormPrim
from omni.physx.scripts import utils
from pxr import UsdPhysics
# Load Object
trimesh_occluder = self._trimesh_occluders[obj_index]
add_reference_to_stage(usd_path=trimesh_occluder.usd_path, prim_path="/World/"+trimesh_occluder.name)
# Make it a rigid body
occluder_prim = self._stage.GetPrimAtPath("/World/"+trimesh_occluder.name)
utils.setRigidBody(occluder_prim, "convexDecomposition", False)
mass_api = UsdPhysics.MassAPI.Apply(occluder_prim)
# Get Bounding Box
bounding_box = trimesh_occluder.bb
self._occluder_top_z =abs(bounding_box[1][1]-bounding_box[0][1])
self._occluder_x_lb = bounding_box[0][0]
self._occluder_x_ub = bounding_box[1][0]
self._occluder_y_lb = bounding_box[0][2]
self._occluder_y_ub = bounding_box[1][2]
# Add to Sim
spawn_z = abs(bounding_box[0][1]) + 0.01
if(spawn_z > self._data_generation_config["max_spawn_z"]):
spawn_z = self._data_generation_config["max_spawn_z"]
self._occluder_spawn_z = spawn_z
carb.log_warn("Spawning Occluder: "+ trimesh_occluder.name+", z: "+str(spawn_z))
self._world.scene.add(XFormPrim(prim_path="/World/"+trimesh_occluder.name,
name=trimesh_occluder.name,
position=[self._occluder_spawn_x, self._occluder_spawn_y, self._occluder_spawn_z],
orientation=[0.7071068, 0.7071068, 0, 0],
scale=self._data_generation_config["model_scaling"]))
def _read_objects(self, dataset_path):
for root, dirs, files in os.walk(dataset_path, topdown=False):
for name in dirs:
path = os.path.join(root, name)
obj_label=self._extract_model_type(path)
obj_name=self._extract_model_name(path)
if(os.path.join(root, name)[-14:] == "Scan_converted"):
# self._objs[path+'/Simp_obj.usd'] = [obj_label, obj_name]
self._trimesh_objects.append(Object(obj_path=path[:-10]+'/Simp.obj',
usd_path = path+'/Simp_obj.usd',
scale=self._data_generation_config["model_scaling"],
eular_angles=[1.57, 0, 0],
label=obj_label,
name=obj_name))
def _read_occluders(self, dataset_path):
for root, dirs, files in os.walk(dataset_path, topdown=False):
for name in dirs:
path = os.path.join(root, name)
occluder_label=self._extract_model_type(path)
occluder_name=self._extract_model_name(path)
if(os.path.join(root, name)[-14:] == "Scan_converted"):
self._trimesh_occluders.append(Object(obj_path=path[:-10]+'/Simp.obj',
usd_path = path+'/Simp_obj.usd',
scale=self._data_generation_config["model_scaling"],
eular_angles=[1.57, 0, 0],
label=occluder_label,
name=occluder_name))
def _spawn_objects(self):
from omni.physx.scripts import utils
from pxr import UsdPhysics
import omni.replicator.core as rep
num_spawned_objects = 0
usd_spawned = []
while(num_spawned_objects < self._data_generation_config["object_num"]):
random_index = random.randint(0, len(self._trimesh_objects)-1)
if(self._trimesh_objects[random_index].usd_path in usd_spawned):
pass
else:
if(self._simulation_app.is_running()):
self._trimesh_objects[random_index]._load_mesh()
if(self._add_object_to_sim(random_index)):
usd_spawned.append(self._trimesh_objects[random_index].usd_path)
num_spawned_objects += 1
self._selected_object_indices.append(random_index)
# Make objects all rigid bodies
for object in self._replicator_objects:
prim_path = rep.utils.get_node_targets(object.node, "inputs:primsIn")[0]
object_prim = self._stage.GetPrimAtPath(prim_path)
utils.setRigidBody(object_prim, "triangleMesh", False)
mass_api = UsdPhysics.MassAPI.Apply(object_prim)
self._step(10)
self._step(100)
def _spawn_occluder(self):
random_index = random.randint(0, len(self._trimesh_occluders)-1)
self._selected_occluder_index = random_index
if(self._simulation_app.is_running()):
self._trimesh_occluders[random_index]._load_mesh()
carb.log_error(self._trimesh_occluders[random_index]._mesh)
self._trimesh_occluders[random_index]._find_placable_areas()
self._add_occluder_to_sim(random_index)
self._step(10)
def _step(self, step_times, render=True, step_sim=True):
for i in range(step_times):
if(self._simulation_app.is_running()):
self._world.step(render=render, step_sim=step_sim)
def _export_usd_scene(self, scene_path, scene_id=0):
self._stage.GetRootLayer().Export(scene_path+"/scene_"+str(scene_id)+"/scene.usd")
def _generate_dataset(self):
c = 0
for r in range(self._num_r):
for h in range(self._num_h):
for v in range(self._num_v):
sampling_radius = self._data_generation_config["min_radius"] + r*self._data_generation_config["dr"]
h_angle_in_rad = (self._data_generation_config["start_h_angle"] + self._data_generation_config["dhfov"] * h) / 180 * math.pi
v_angle_in_rad = (0 + self._data_generation_config["dvfov"] * v) / 180 * math.pi
look_at_position = (self._data_generation_config["look_at_position"][0]+((random.random()-0.5)*self._data_generation_config["look_at_noise"]),
self._data_generation_config["look_at_position"][1]+((random.random()-0.5)*self._data_generation_config["look_at_noise"]),
self._data_generation_config["look_at_position"][2]+((random.random()-0.5)*self._data_generation_config["look_at_noise"]),)
# carb.log_warn("r: "+str(sampling_radius)+", h: "+str(h_angle_in_rad)+", v: "+str(v_angle_in_rad))
px = self._data_generation_config["look_at_position"][0] + sampling_radius*math.sin(v_angle_in_rad)*math.cos(h_angle_in_rad)
py = self._data_generation_config["look_at_position"][1] + sampling_radius*math.cos(v_angle_in_rad)*math.cos(h_angle_in_rad)
pz = self._data_generation_config["look_at_position"][2] + sampling_radius*math.sin(h_angle_in_rad)
dx = look_at_position[0] - px
dy = look_at_position[1] - py
dz = look_at_position[2] - pz
roll = (random.random()-0.5)*30
pitch = math.atan2(dz, math.sqrt(dx**2+dy**2))/math.pi*180
yaw = math.atan2(-dy, -dx)/math.pi*180
_camera_prim = self._stage.GetPrimAtPath("/Replicator/DriverCam_Xform")
_camera_prim.GetAttribute("xformOp:translate").Set((px, py, pz))
_camera_prim.GetAttribute("xformOp:rotateXYZ").Set((roll, pitch, yaw))
carb.log_warn("frame: "+str(c)+"x: "+str(px)+", y: "+str(py)+", z: "+str(pz)+", roll: "+str(roll)+", pitch: "+str(pitch)+", yaw: "+str(yaw))
self._rep.orchestrator.step()
self._step(1)
c+=1
def _is_scene_valid(self):
trimesh_occluder = self._trimesh_occluders[self._selected_occluder_index]
occluder_prim_path = "/World/"+trimesh_occluder.name
occluder_dc_object = self._dc.get_rigid_body(occluder_prim_path)
occluder_pose = self._dc.get_rigid_body_pose(occluder_dc_object)
occluder_actual_position = occluder_pose.p
occluder_reference_position = [self._occluder_spawn_x, self._occluder_spawn_y, self._occluder_spawn_z]
# carb.log_error("current occluder pose x: "+str(occluder_pose.p[0])+
# ", current occluder pose y: "+str(occluder_pose.p[1])+
# ", current occluder pose z: "+str(occluder_pose.p[2]))
# carb.log_error("reference occluder pose x: "+str(self._occluder_spawn_x)+
# ", reference occluder pose y: "+str(self._occluder_spawn_y)+
# ", reference occluder pose z: "+str(self._occluder_spawn_z))
distance = distance_3d(occluder_actual_position, occluder_reference_position)
carb.log_warn("distance between occluder's reference and actual positions: "+str(distance))
if(distance >= self._data_generation_config["distance_threshold"]):
carb.log_error("Occluder is moved too much after spawning objects, scene generation failed!")
return False
return True
def _refresh_objects_and_occluders(self):
self._occluder_top_z = 0
self._occluder_spawn_x = 0
self._occluder_spawn_y = 0
self._occluder_spawn_z = 0
self._occluder_x_lb = 0
self._occluder_x_ub = 0
self._occluder_y_lb = 0
self._occluder_y_ub = 0
self._selected_occluder_index = 0
self._selected_object_indices = []
self._replicator_objects = []
def _main(self):
self._add_background(self._data_generation_config["background_path"])
self._read_objects(self._data_generation_config["object_dataset_path"])
self._read_occluders(self._data_generation_config["occluder_dataset_path"])
# c = 0
# while(c < self._data_generation_config["num_scenes"]):
# self._spawn_occluder()
# self._spawn_objects()
# if(self._is_scene_valid()):
# self._setup_replicator(scene_id=c)
# self._generate_dataset()
# self._export_usd_scene(self._data_generation_config["data_save_path"], scene_id=c)
# self._refresh_objects_and_occluders()
self._spawn_occluder()
self._spawn_objects()
self._setup_replicator(scene_id=0)
self._generate_dataset()
self._export_usd_scene(self._data_generation_config["data_save_path"], scene_id=0)
self._step(10000)
self._simulation_app.close()
abba = GENERATE_BOJECTS()

View File

@@ -0,0 +1,290 @@
import numpy as np
import trimesh
from sklearn import linear_model
import copy
import math
def distance_3d(a, b):
return np.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2)
class CONVEX_HULL:
def __init__(self, points) -> None:
self.average_z = 0
self.highest_z = -np.inf
self.convex_hull = self.polygon_scan(points)
self.calc_average_z()
self.calc_highest_z()
def distance(self, a, b):
return np.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
def cross_product(self, a, b, c):
return (b[1]-a[1])*(c[0]-b[0]) - (b[0]-a[0])*(c[1]-b[1])
def dot_product(self, a, b, c):
ab = [b[0] - a[0], b[1] - a[1]]
bc = [c[0] - b[0], c[1] - b[1]]
return ab[0] * bc[0] + ab[1] * bc[1]
def angle(self, a, b, c):
cp = self.cross_product(a, b, c)
dp= self.dot_product(a, b, c)
angle_radians = math.atan2(cp, dp)
angle_degrees = math.degrees(angle_radians)
return angle_degrees
def new_point_orientation(self, pivot, p1, p2):
orientation = self.cross_product(pivot, p1, p2)
if orientation == 0:
return 0
elif(orientation > 0):
return 1
else:
return -1
def polygon_scan(self, points_in):
points = copy.deepcopy(points_in)
n = len(points)
if n < 3:
print("Convec Hull required at least 3 points.")
return
else:
if(n==3 and self.new_point_orientation[points[0],points[1],points[2]]==0):
print("3 points are in one line.")
return
else:
pivot_index, pivot_point = min(enumerate(points), key=lambda point: point[1]) # 优先根据y值排序若y相同则找到相同y下x值最小的点
start_point = copy.deepcopy(pivot_point)
start_point[1] -= 1
convex_hull = [start_point, pivot_point]
closed = False
while not closed:
max_x = -np.inf
max_x_idx = 0
for p_idx in range(len(points)):
point = points[p_idx]
x = self.angle(convex_hull[-2], convex_hull[-1], point)
if(x > max_x and x != 180 and x != 0):
max_x = x
max_x_idx = p_idx
if(len(points) == 0):
closed = True
else:
new_point = points[max_x_idx]
if(new_point[0] == pivot_point[0] and new_point[1] == pivot_point[1]):
closed = True
else:
convex_hull.append(new_point)
points.pop(max_x_idx)
convex_hull.pop(0)
return convex_hull
def is_point_inside_convex_hull(self, point_in):
point = copy.deepcopy(point_in)
new_convex_hull_points = copy.deepcopy(self.convex_hull)
new_convex_hull_points.append(point)
new_convex_hull = self.polygon_scan(new_convex_hull_points)
ans = (new_convex_hull == self.convex_hull)
return ans
def are_points_inside_convex_hull(self, points_in):
points = copy.deepcopy(points_in)
ans = True
for point in points:
p_ans = self.is_point_inside_convex_hull(point)
ans = ans * p_ans
return ans
def get_convex_hull(self):
return self.convex_hull
def calc_average_z(self):
for p in self.convex_hull:
self.average_z += p[2]/len(self.convex_hull)
def get_average_z(self):
return self.average_z
def calc_highest_z(self):
for p in self.convex_hull:
if(p[2]>self.highest_z):
self.highest_z = p[2]
def get_highest_z(self):
return self.highest_z
class Object:
def __init__(self, obj_path, usd_path=None, scale=[1, 1, 1], eular_angles=[0, 0, 0], max_num_planes=2,
label=None, name=None, ) -> None:
# trimesh
self.obj_path = obj_path
self.usd_path = usd_path
self.scale = scale
self.eular_angles = eular_angles
self.label = label
self.name = name
self.bb = None
# get placable points
self.placable_points = []
# ransac
self.max_num_planes = max_num_planes
self.X = []
self.y = []
self.plane_point_groups = []
self.plane_coef_groups = []
# convex_hull
self.convex_hulls = []
self.convex_hulls_z = []
def _load_mesh(self):
# Load mesh
self._mesh = trimesh.load(self.obj_path)
self._mesh.apply_scale(self.scale)
# Get Bounding Box
self.bb = self._mesh.bounds
# Rotate Mesh
self._rotate_mesh(self.eular_angles)
def _find_placable_areas(self):
# Find placable points
self._get_placable_points()
self._convert_placable_points_to_sklearn_X_y()
self._get_plane_point_groups()
self._get_convex_hulls()
def _rotate_mesh(self, rot_rpy):
center = [0, 0, 0]
angle = rot_rpy[0]
direction = [1, 0, 0]
rot_matrix_r = trimesh.transformations.rotation_matrix(angle, direction, center)
self._mesh.apply_transform(rot_matrix_r)
angle = rot_rpy[1]
direction = [0, 1, 0]
rot_matrix_p = trimesh.transformations.rotation_matrix(angle, direction, center)
self._mesh.apply_transform(rot_matrix_p)
angle = rot_rpy[2]
direction = [0, 0, 1]
rot_matrix_y = trimesh.transformations.rotation_matrix(angle, direction, center)
self._mesh.apply_transform(rot_matrix_y)
def _get_placable_points(self):
gravity=np.array([0, 0, -1.0])
support_facet_indices = np.argsort(self._mesh.facets_area)
for idx in support_facet_indices:
if(np.isclose(self._mesh.facets_normal[idx].dot(gravity), 1.0, atol=0.15)):
facet_boundary = self._mesh.facets_boundary[idx]
for bondary_vertices_indices in facet_boundary:
for boundary_vertex_index in bondary_vertices_indices:
point = self._mesh.vertices[boundary_vertex_index]
self.placable_points.append(list(point))
return self.placable_points
def _convert_placable_points_to_sklearn_X_y(self):
for p in self.placable_points:
self.X.append([p[0], p[1]])
self.y.append(p[2])
self.X = np.array(self.X)
self.y = np.array(self.y)
return self.X, self.y
def _get_plane_point_groups(self):
ransac = linear_model.RANSACRegressor()
num_planes = 0
while(num_planes < self.max_num_planes):
plane_points = []
plane_coefs = []
num_popped_element = 0
if(len(self.X) <= 2):
break
else:
ransac.fit(self.X, self.y)
inlier_mask = ransac.inlier_mask_ # a list of bool values indicating whether the element is inlier point
plane_coefficients = ransac.estimator_.coef_
plane_intercept = ransac.estimator_.intercept_
plane_coefs = [plane_coefficients[0], plane_coefficients[1], plane_intercept]
for i in range(len(self.X)):
if(len(self.X) > 0 and inlier_mask[i] == True):
X = self.X[i - num_popped_element]
y = self.y[i - num_popped_element]
plane_points.append([X[0], X[1], y])
self.X = np.delete(self.X, i-num_popped_element, 0)
self.y = np.delete(self.y, i-num_popped_element, 0)
num_popped_element+=1
self.plane_point_groups.append(plane_points)
self.plane_coef_groups.append(plane_coefs)
num_planes += 1
return self.plane_point_groups, self.plane_coef_groups
def _get_convex_hulls(self):
for plane_points in self.plane_point_groups:
ch = CONVEX_HULL(plane_points)
self.convex_hulls.append(ch)
class BOUNDING_BOX_2D:
def __init__(self, origin, width, height, rot_z=0) -> None:
self.origin = origin
self.origin_x = origin[0]
self.origin_y = origin[1]
self.width = width
self.height = height
self.rot_z = rot_z
self.setup()
def setup(self):
self._get_bb_vertices()
self._apply_transform_to_bb_vertices()
def _get_bb_vertices(self):
self.bb_vertics = [[self.origin_x-self.width/2, self.origin_y-self.height/2],
[self.origin_x-self.width/2, self.origin_y+self.height/2],
[self.origin_x+self.width/2, self.origin_y-self.height/2],
[self.origin_x+self.width/2, self.origin_y+self.height/2],]
return self.bb_vertics
def _apply_transform_to_bb_vertices(self):
origin = np.array([[1, 0, 0, self.origin_x],
[0, 1, 0, self.origin_y],
[0, 0, 1, 0],
[0, 0, 0, 1]])
T = np.array([[np.cos(self.rot_z), -np.sin(self.rot_z), 0, 0],
[np.sin(self.rot_z), np.cos(self.rot_z), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]])
T_left_up = np.array([[1, 0, 0, -self.width/2],
[0, 1, 0, self.height/2],
[0, 0, 1, 0],
[0, 0, 0, 1]])
T_left_down = np.array([[1, 0, 0, -self.width/2],
[0, 1, 0, -self.height/2],
[0, 0, 1, 0],
[0, 0, 0, 1]])
T_right_up = np.array([[1, 0, 0, self.width/2],
[0, 1, 0, self.height/2],
[0, 0, 1, 0],
[0, 0, 0, 1]])
T_right_down = np.array([[1, 0, 0, self.width/2],
[0, 1, 0, -self.height/2],
[0, 0, 1, 0],
[0, 0, 0, 1]])
transformed_origin = origin.dot(T)
tv0 = transformed_origin.dot(T_left_down)
tv1 = transformed_origin.dot(T_left_up)
tv2 = transformed_origin.dot(T_right_down)
tv3 = transformed_origin.dot(T_right_up)
transformed_vertices = [tv0, tv1, tv2, tv3]
for vertex_idx in range(len(self.bb_vertics)):
self.bb_vertics[vertex_idx][0] = transformed_vertices[vertex_idx][0][3]
self.bb_vertics[vertex_idx][1] = transformed_vertices[vertex_idx][1][3]

View File

@@ -0,0 +1,83 @@
import argparse
import asyncio
import os
import omni
from omni.isaac.kit import SimulationApp
async def convert(in_file, out_file, load_materials=True):
# This import causes conflicts when global
import omni.kit.asset_converter
def progress_callback(progress, total_steps):
pass
converter_context = omni.kit.asset_converter.AssetConverterContext()
# setup converter and flags
converter_context.ignore_materials = not load_materials
# converter_context.ignore_animation = False
# converter_context.ignore_cameras = True
# converter_context.single_mesh = True
# converter_context.smooth_normals = True
# converter_context.preview_surface = False
# converter_context.support_point_instancer = False
# converter_context.embed_mdl_in_usd = False
# converter_context.use_meter_as_world_unit = True
# converter_context.create_world_as_default_root_prim = False
instance = omni.kit.asset_converter.get_instance()
task = instance.create_converter_task(in_file, out_file, progress_callback, converter_context)
success = True
while True:
success = await task.wait_until_finished()
if not success:
await asyncio.sleep(0.1)
else:
break
return success
def asset_convert(folders):
supported_file_formats = ["stl", "obj", "fbx"]
for folder in folders:
local_asset_output = folder + "_converted"
result = omni.client.create_folder(f"{local_asset_output}")
for folder in folders:
print(f"\nConverting folder {folder}...")
(result, models) = omni.client.list(folder)
for i, entry in enumerate(models):
model = str(entry.relative_path)
model_name = os.path.splitext(model)[0]
model_format = (os.path.splitext(model)[1])[1:]
# Supported input file formats
if model_format in supported_file_formats:
input_model_path = folder + "/" + model
converted_model_path = folder + "_converted/" + model_name + "_" + model_format + ".usd"
if not os.path.exists(converted_model_path):
status = asyncio.get_event_loop().run_until_complete(
convert(input_model_path, converted_model_path, True)
)
if not status:
print(f"ERROR Status is {status}")
print(f"---Added {converted_model_path}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('dataset_path', type=str, help='path to the dataset')
args = parser.parse_args()
folders = []
for root, dirs, files in os.walk(args.dataset_path, topdown=False):
for name in dirs:
path = os.path.join(root, name)
if(os.path.join(root, name)[-4:] == "Scan"):
folders.append(path)
kit = SimulationApp()
from omni.isaac.core.utils.extensions import enable_extension
enable_extension("omni.kit.asset_converter")
asset_convert(folders)
kit.close()

240
data_generation/tools/get_view.py Executable file
View File

@@ -0,0 +1,240 @@
import os
import pickle
import pybullet as p
import time
import pybullet_data
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import open3d as o3d
import cv2
import math
import json
class GenerateScene:
def __init__(self, dataset_path, scene_path, output_path, camera_params) -> None:
self._init_variables()
self._load_object_dataset(dataset_path)
self._load_scene(scene_path)
self._set_output_path(output_path)
self._set_camera_params(camera_params)
def _init_variables(self):
self.object_paths = {}
self.scene = {}
self.object_model_scale = [0.001, 0.001, 0.001]
self.output_path = None
self.camera_params = None
self.segmentation_labels = {}
def _extract_model_name(self, path):
# Specify it according to specific file directory
NUM_SLASH_BEFORE_NAME = 1
num_slash = 0
object_name_str = []
for i in range(len(path)):
index = len(path) -1 - i
char = path[index]
if(num_slash == NUM_SLASH_BEFORE_NAME):
object_name_str.append(char)
if(char == "/"):
num_slash += 1
object_name_str.reverse()
object_name_str = ''.join(object_name_str[1:])
return object_name_str
def _load_object_dataset(self, dataset_path):
if(dataset_path[-1] == "/"):
dataset_path = dataset_path[:-1]
for root, dirs, files in os.walk(dataset_path, topdown=False):
for name in dirs:
path = os.path.join(root, name)
if(os.path.join(root, name)[-4:] == "Scan"):
name = self._extract_model_name(path)
self.object_paths[name] = path+"/Simp.obj"
def _load_scene(self, scene_path):
if(scene_path[-1] == "/"):
scene_path = scene_path[:-1]
scene_path = scene_path + "/scene.pickle"
self.scene = pickle.load(open(scene_path, 'rb'))
def _set_output_path(self, output_path):
self.output_path = output_path
if(self.output_path[-1] == "/"):
self.output_path = self.output_path[:-1]
def _set_camera_params(self, camera_params):
self.camera_params = camera_params
def load_camera_pose_from_frame(self, camera_params_path):
with open(camera_params_path, "r") as f:
camera_params = json.load(f)
view_transform = camera_params["cameraViewTransform"]
print(view_transform)
view_transform = np.resize(view_transform, (4,4))
view_transform = np.linalg.inv(view_transform).T
offset = np.mat([[1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,1]])
view_transform = view_transform.dot(offset)
print(view_transform)
return view_transform
def _load_obj_to_pybullet(self, obj_file_path, position, orientation, scale):
visual_ind = p.createVisualShape(
shapeType=p.GEOM_MESH,
fileName=obj_file_path,
rgbaColor=[1, 1, 1, 1],
specularColor=[0.4, 0.4, 0],
visualFramePosition=[0, 0, 0],
meshScale=scale)
p.createMultiBody(
baseMass=1,
baseVisualShapeIndex=visual_ind,
basePosition=position,
baseOrientation=orientation,
useMaximalCoordinates=True)
def _render_image(self, camera_pose):
width = self.camera_params["width"]
height = self.camera_params["height"]
fov = self.camera_params["fov"]
aspect = width / height
near = self.camera_params["near"]
far = self.camera_params["far"]
T = np.mat([[1,0,0,0],
[0,1,0,0],
[0,0,1,1],
[0,0,0,1]])
look_at_T = camera_pose.dot(T)
view_matrix = p.computeViewMatrix([camera_pose[0,3], camera_pose[1,3], camera_pose[2,3]],
[look_at_T[0,3], look_at_T[1,3], look_at_T[2,3]],
[-camera_pose[0,1], -camera_pose[1,1], -camera_pose[2,1]])
projection_matrix = p.computeProjectionMatrixFOV(fov, aspect, near, far)
# Get depth values using the OpenGL renderer
images = p.getCameraImage(width, height, view_matrix, projection_matrix, renderer=p.ER_BULLET_HARDWARE_OPENGL)
rgb = images[2]
depth = images[3]
seg = images[4]
rgb_image = cv2.cvtColor(rgb, cv2.COLOR_BGR2RGB)
cv2.imwrite(self.output_path+'/rgb.jpg',rgb_image)
depth_image = far * near / (far - (far - near) * depth)
depth_image = np.asanyarray(depth_image).astype(np.float32) * 1000.0
depth_image_array = depth_image
depth_image = (depth_image.astype(np.uint16))
depth_image = Image.fromarray(depth_image)
depth_image.save(self.output_path+'/depth.png')
cv2.imwrite(self.output_path+'/seg.jpg', seg)
id = 0
for object_name in self.scene.keys():
self.segmentation_labels[str(id+1)] = object_name
id += 1
with open(self.output_path+"/seg_labels.json", 'w') as seg_labels:
json.dump(self.segmentation_labels, seg_labels)
with open(self.output_path+"/cam_intrinsics.json", 'w') as cam_intrinsics:
json.dump(self.camera_params, cam_intrinsics)
def generate_images(self, camera_pose):
physicsClient = p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0,0,0)
p.loadURDF("plane100.urdf")
for obj_name in self.scene.keys():
orientation = self.scene[obj_name]["rotation"]
position = self.scene[obj_name]["position"]
self._load_obj_to_pybullet(obj_file_path=self.object_paths[obj_name],
position=position,
orientation=orientation,
scale=self.object_model_scale)
self._render_image(camera_pose)
p.stepSimulation()
p.disconnect()
def visualize_pcd(self):
color_image = o3d.io.read_image(self.output_path+'/rgb.jpg')
depth_image = o3d.io.read_image(self.output_path+'/depth.png')
rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(color_image, depth_image,convert_rgb_to_intensity=False)
intrinsic = o3d.camera.PinholeCameraIntrinsic(
o3d.camera.PinholeCameraIntrinsicParameters.Kinect2DepthCameraDefault )
intrinsic.set_intrinsics(width=self.camera_params["width"],
height=self.camera_params["height"],
fx=self.camera_params["fx"],
fy=self.camera_params["fy"],
cx=self.camera_params["cx"],
cy=self.camera_params["cy"])
point_cloud = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd_image, intrinsic)
o3d.visualization.draw_geometries([point_cloud])
def print_scene(self):
print("================= Scene Objects: =================")
print(self.scene.keys())
print("==================================================")
DATASET_PATH = "/home/hzx/Downloads/OmniObject3d-simplified/output"
SCENE_PATH = "/home/hzx/Projects/ActivePerception/data_generation/output/scene_0/"
OUTPUT_PATH = "/home/hzx/Projects/ActivePerception/data_generation/output/"
FRAME_PATH = "/home/hzx/Projects/ActivePerception/data_generation/output/scene_0/camera_params_0119.json"
ISAAC_SIM_CAM_H_APERTURE = 20.955 # Isaac Sim里读取的
ISAAC_SIM_CAM_V_APERTURE = 15.2908 # Isaac Sim里读取的
ISAAC_SIM_FOCAL_LENGTH = 39 # 试出来的其实Isaac Sim里原本是24
ISAAC_SIM_CAM_D_APERTURE = math.sqrt(ISAAC_SIM_CAM_H_APERTURE**2 + ISAAC_SIM_CAM_V_APERTURE**2)
CAM_WIDTH = 640
CAM_HEIGHT = 480
CAM_FOV = 2*math.atan(ISAAC_SIM_CAM_D_APERTURE/(2*ISAAC_SIM_FOCAL_LENGTH))/math.pi*180
CAM_NEAR = 0.001 # 成像最近距离
CAM_FAR = 10 # 成像最远距离
CAM_CX = CAM_WIDTH/2
CAM_CY = CAM_HEIGHT/2
CAM_FX = 1/math.tan(CAM_FOV*math.pi/180.0/2.0)*CAM_WIDTH/2
CAM_FY = 1/(CAM_HEIGHT/CAM_WIDTH*math.tan(CAM_FOV*math.pi/180.0/2.0))*CAM_HEIGHT/2
CAMERA_PARAMS = {
"width": CAM_WIDTH,
"height": CAM_HEIGHT,
"fov": CAM_FOV,
"near": CAM_NEAR,
"far": CAM_FAR,
"cx": CAM_CX,
"cy": CAM_CY,
"fx": CAM_FX,
"fy": CAM_FY,
}
if __name__ == "__main__":
gs = GenerateScene(DATASET_PATH, SCENE_PATH, OUTPUT_PATH, CAMERA_PARAMS)
gs.print_scene()
cam_pose = gs.load_camera_pose_from_frame(FRAME_PATH)
gs.generate_images(cam_pose) # 在OUTPUT_PATH路径下生成rgb、depth、segmentation图
# gs.visualize_pcd()