Skip to content

Commit a7c3516

Browse files
committed
merge
2 parents 230f433 + 04fed4b commit a7c3516

File tree

11 files changed

+482
-29
lines changed

11 files changed

+482
-29
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,16 @@ dmypy.json
131131

132132
.vscode/
133133

134+
debug/
134135
outputs/
135136
data/
136137
multirun/
137138
wandb/
138139
*.out
139140
*.sl
141+
*.pcd
142+
*.ply
143+
140144
speed-tree-outputs/
141145
smart_tree/conf/tree-dataset-test.yaml
142146
smart_tree/conf/tree-split-test.json
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
training:
2+
3+
name: "tree"
4+
wblogging: "online"
5+
fp16: False
6+
num_epoch: 10
7+
lr_decay: True
8+
early_stop_epoch: 20
9+
early_stop: True
10+
use_colour: False
11+
12+
dataset:
13+
_target_: smart_tree.dataset.dataset.TreeDataset
14+
voxel_size: 0.01
15+
json_path: /local/uc-vision/smart-tree/smart_tree/conf/tree-split.json
16+
directory: /local/uc-vision/dataset/branches/
17+
blocking: True
18+
block_size: 4
19+
buffer_size: 0.4
20+
21+
# augmentation:
22+
# _target_: smart_tree.dataset.augmentations.Scale
23+
# min_scale: 0.9
24+
# max_scale: 1.1
25+
26+
# _target_: smart_tree.dataset.augmentations.RandomDropout
27+
# max_drop_out: 0.1
28+
29+
# _target_: smart_tree.dataset.augmentations.RandomColourDropout
30+
# max_drop_out: 0.1
31+
32+
data_loader:
33+
_target_: torch.utils.data.DataLoader
34+
batch_size: 16
35+
drop_last: True
36+
pin_memory: False
37+
num_workers: 0
38+
#prefetch_factor:
39+
shuffle: True
40+
collate_fn:
41+
_target_: smart_tree.model.sparse.batch_collate
42+
_partial_: True
43+
44+
model:
45+
_target_: smart_tree.model.model.Smart_Tree
46+
input_channels: 3
47+
unet_planes: [8, 16, 32, 64, 128, 256]
48+
radius_fc_planes: [8, 8, 4, 1]
49+
direction_fc_planes: [8, 8, 4, 3]
50+
class_fc_planes: [8, 8, 4, 2] # last one is number of classes..
51+
bias: False
52+
53+
optimizer:
54+
_target_: torch.optim.Adam # SGD, Adadelta etc
55+
lr: 0.1
56+
#eps: 0.00001
57+
58+
59+
scheduler:
60+
_target_: torch.optim.lr_scheduler.ReduceLROnPlateau
61+
mode: "min"
62+
63+
64+
model_inference:
65+
model_path: /local/uc-vision/smart-tree/smart_tree/model/weights/noble-elevator-58_model.pt
66+
weights_path: /local/uc-vision/smart-tree/smart_tree/model/weights/noble-elevator-58_model_weights.pt
67+
voxel_size: 0.01
68+
block_size: 4
69+
buffer_size: 0.4
70+
num_workers : 8
71+
batch_size : 4
72+
use_colour: False
73+
74+
skeletonizer:
75+
K: 16
76+
min_connection_length: 0.00 #.02
77+
minimum_graph_vertices: 32
78+
max_number_components: 30
79+
voxel_downsample: False
80+
edge_non_linear: None
81+
82+
83+
pipeline:
84+
85+
preprocessing:
86+
# Scale:
87+
# _target_: smart_tree.dataset.augmentations.Scale
88+
# min_scale: 1
89+
# max_scale: 1
90+
# Scale:
91+
# _target_: smart_tree.dataset.augmentations.Scale
92+
# min_scale: 1
93+
# max_scale: 1
94+
# VoxelDownsample:
95+
# _target_: smart_tree.dataset.augmentations.VoxelDownsample
96+
# voxel_size : 0.01
97+
98+
# FixedRotate:
99+
# _target_: smart_tree.dataset.augmentations.FixedRotate
100+
# xyz: [0, 0, 90]
101+
# CentreCloud:
102+
# _target_: smart_tree.dataset.augmentations.CentreCloud
103+
104+
105+
repair_skeletons : True
106+
smooth_skeletons : True
107+
prune_skeletons : True
108+
min_skeleton_radius : 0.001 #0.005
109+
min_skeleton_length : 0.02
110+
view_model_output : True
111+
view_skeletons : True
112+
save_outputs : False
113+
branch_classes: [0]
114+
cmap:
115+
- [1, 0, 0] # Trunk
116+
- [0, 1, 0] # Foliage

smart_tree/data_types/cloud.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,17 @@ def max_xyz(self):
7575
def min_xyz(self):
7676
return torch.min(self.xyz, 0)[0]
7777

78+
@property
79+
def centre(self):
80+
return self.min_xyz + (self.max_xyz - self.min_xyz) / 2
81+
82+
@property
83+
def dimensions(self):
84+
return self.max_xyz - self.min_xyz
85+
7886
@property
7987
def bbox(self):
80-
# defined by centre coordinate, x/2, y/2, z/2
81-
dimensions = (self.max_xyz - self.min_xyz) / 2
82-
centre = self.min_xyz + dimensions
83-
return centre, dimensions
88+
return self.centre, self.dimensions
8489

8590
@staticmethod
8691
def from_numpy(xyz, rgb, device=torch.device("cpu")):
@@ -99,9 +104,21 @@ class LabelledCloud(Cloud):
99104
vector: torch.Tensor
100105
class_l: torch.Tensor
101106

102-
def __post_init__(self):
103-
num_classes = int(torch.max(self.class_l, 0)[0].item())
104-
self.cmap = torch.rand(num_classes + 1, 3)
107+
@property
108+
def number_classes(self):
109+
return int(torch.max(self.class_l, 0)[0].item()) + 1
110+
111+
@property
112+
def cmap(self):
113+
return torch.rand(self.number_classes, 3)
114+
115+
def __add__(self, other):
116+
xyz = torch.cat((self.xyz, other.xyz))
117+
rgb = torch.cat((self.rgb, other.rgb))
118+
vector = torch.cat((self.vector, other.vector))
119+
class_l = torch.cat((self.class_l, other.class_l))
120+
121+
return LabelledCloud(xyz, rgb, vector, class_l)
105122

106123
def filter(self, mask):
107124
return LabelledCloud(
@@ -198,3 +215,12 @@ def from_numpy(xyz, rgb, vector, class_l):
198215
torch.from_numpy(vector).float(), # float32
199216
torch.from_numpy(class_l).int(), # int64
200217
)
218+
219+
@staticmethod
220+
def from_o3d_cld(cld, class_l):
221+
return LabelledCloud.from_numpy(
222+
xyz=np.asarray(cld.points),
223+
rgb=np.asarray(cld.colors),
224+
vector=np.asarray(cld.points) * 0 - 1,
225+
class_l=np.asarray(class_l),
226+
)

smart_tree/dataset/augmentations.py

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
import os
12
import random
23

34
import numpy as np
45
import torch
6+
import random
7+
import open3d as o3d
58

9+
from tqdm import tqdm
10+
from pathlib import Path
611
from abc import ABC, abstractmethod
712
from typing import List
813

9-
from smart_tree.data_types.cloud import Cloud
14+
from smart_tree.data_types.cloud import Cloud, LabelledCloud
1015
from smart_tree.util.math.maths import euler_angles_to_rotation
16+
from smart_tree.util.file import (
17+
load_o3d_mesh,
18+
save_o3d_mesh,
19+
save_o3d_cloud,
20+
load_o3d_cloud,
21+
)
22+
from smart_tree.util.mesh.geometries import o3d_cloud, o3d_sphere
1123
from hydra.utils import call, get_original_cwd, instantiate, to_absolute_path
1224

1325

@@ -73,6 +85,89 @@ def __call__(self, cloud):
7385
)
7486

7587

88+
class RandomMesh(Augmentation):
89+
def __init__(
90+
self,
91+
mesh_directory: Path,
92+
preprocessed_path: Path,
93+
voxel_size: float,
94+
number_meshes: int,
95+
min_size: float,
96+
max_size: float,
97+
max_pts: int,
98+
):
99+
"""We want to preprocess the meshes by getting them to the right scale,
100+
which is done by first converting them from mm to metres, we then
101+
scale them up to the max_size and then do a point sample, based on target voxel_size
102+
(ensure we have enough point density at the max size), then revert
103+
the scale back normal scale in metres. During inference we randomly scale based on the
104+
min_size and max_size and then translate the points and merge with the input cloud
105+
"""
106+
107+
self.voxel_size = voxel_size
108+
self.number_meshes = number_meshes
109+
self.min_size = min_size
110+
self.max_size = max_size
111+
self.class_number = 2
112+
self.preprocessed_path = preprocessed_path
113+
114+
if not (os.path.exists(preprocessed_path)):
115+
os.makedirs(preprocessed_path)
116+
117+
for mesh_path in tqdm(
118+
Path(mesh_directory).glob("*.stl"),
119+
desc="Preprocessing Meshes",
120+
leave=False,
121+
):
122+
if os.path.isfile(f"{preprocessed_path}/{mesh_path.stem}.pcd"):
123+
continue
124+
try:
125+
mesh = load_o3d_mesh(str(mesh_path))
126+
pcd = (
127+
mesh.scale(0.001, mesh.get_center())
128+
.translate(-mesh.get_center())
129+
.paint_uniform_color(np.random.rand(3))
130+
.scale(max_size, mesh.get_center())
131+
.sample_points_uniformly(
132+
min(
133+
max(int(mesh.get_surface_area() / (voxel_size**2)), 10),
134+
max_pts,
135+
)
136+
)
137+
.scale(1 / max_size, mesh.get_center())
138+
)
139+
save_o3d_cloud(f"{preprocessed_path}/{mesh_path.stem}.pcd", pcd)
140+
except:
141+
print(f"Cloud Generation Failed on {mesh_path}")
142+
143+
def __call__(self, cloud):
144+
centre, dimensions = cloud.bbox
145+
146+
for i in range(self.number_meshes):
147+
random_pcd_path = random.choice(list(self.preprocessed_path.glob("*.pcd")))
148+
pcd = load_o3d_cloud(str(random_pcd_path))
149+
scaled_pcd = pcd.scale(
150+
random.uniform(self.min_size, self.max_size), pcd.get_center()
151+
)
152+
153+
lc = LabelledCloud.from_o3d_cld(
154+
pcd,
155+
class_l=torch.ones(np.asarray(pcd.points).shape[0]) * self.class_number,
156+
)
157+
158+
lc = lc.rotate(
159+
euler_angles_to_rotation(torch.rand(3) * torch.pi * 2).to(
160+
cloud.xyz.device
161+
)
162+
)
163+
lc = lc.translate(cloud.min_xyz - lc.centre)
164+
lc = lc.translate(dimensions * torch.rand(3))
165+
166+
cloud += lc
167+
168+
return cloud
169+
170+
76171
class RandomDropout(Augmentation):
77172
def __init__(self, max_drop_out):
78173
self.max_drop_out = max_drop_out
@@ -127,13 +222,36 @@ def from_cfg(cfg):
127222

128223
if __name__ == "__main__":
129224
from pathlib import Path
130-
from smart_tree.util.file import load_cloud
225+
from smart_tree.util.file import load_data_npz
131226
from smart_tree.util.visualizer.view import o3d_viewer
132227

133-
cld = load_cloud(
134-
Path("/media/harry/harry's-data/PhD/training-data/apple/apple_1.npz")
228+
mesh_adder = RandomMesh(
229+
mesh_directory=Path("/local/Datasets/Thingi10K/raw_meshes/"),
230+
preprocessed_path=Path(
231+
"/local/uc-vision/smart-tree/data/things10K_sampled_1mm/"
232+
),
233+
voxel_size=0.001,
234+
number_meshes=20,
235+
min_size=0.01,
236+
max_size=20,
237+
max_pts=50000,
238+
)
239+
240+
cld, _ = load_data_npz(Path("/local/_smart-tree/evaluation-data/gt/apple_12.npz"))
241+
242+
cld = mesh_adder(cld)
243+
244+
cld.view()
245+
246+
o3d_viewer(
247+
[
248+
cld.to_o3d_cld(),
249+
o3d_sphere(cld.min_xyz, radius=0.1, colour=(0, 1, 0)),
250+
o3d_sphere(cld.max_xyz, radius=0.1),
251+
]
135252
)
136253

254+
quit()
137255
centre = CentreCloud()
138256
rotater = FixedRotate(torch.tensor([torch.pi / 2, torch.pi / 2, torch.pi * 2]))
139257
do = RandomDropout(0.5)

smart_tree/dataset/dataset.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ def __getitem__(self, idx):
5151

5252
labelled_cld = labelled_cld.to_device(torch.device("cuda"))
5353

54-
if self.augmentation != None:
55-
labelled_cld = self.augmentation(labelled_cld)
56-
5754
if self.blocking:
5855
block_center_idx = torch.randint(
5956
labelled_cld.xyz.shape[0], size=(1,), device=labelled_cld.xyz.device
@@ -66,6 +63,9 @@ def __getitem__(self, idx):
6663
)
6764
labelled_cld = labelled_cld.filter(block_filter)
6865

66+
if self.augmentation != None:
67+
labelled_cld = self.augmentation(labelled_cld)
68+
6969
xyzmin, _ = torch.min(labelled_cld.xyz, axis=0)
7070
xyzmax, _ = torch.max(labelled_cld.xyz, axis=0)
7171
make_voxel_gen = time.time()

0 commit comments

Comments
 (0)