Skip to content

Commit 887ae46

Browse files
committed
merge
2 parents a7c3516 + 525869d commit 887ae46

File tree

11 files changed

+96
-116
lines changed

11 files changed

+96
-116
lines changed

pyproject.toml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,28 @@ requires = ["setuptools", "setuptools-scm"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
6-
name = "Smart-Tree"
6+
name = "smart-tree"
7+
version = "1.0.0"
78
authors = [
89
{name = "Harry Dobbs", email = "harrydobbs87@gmail.com"},
910
]
1011
description = "Neural Network Point Cloud Tree Skeletonization"
11-
readme = "README.rst"
12+
readme = "README.md"
1213
requires-python = ">=3.8"
1314
license = {text = "BSD-3"}
1415
dependencies = [
1516
'numpy',
1617
'open3d',
1718
'hydra-core>=1.2.0',
1819
'click',
19-
# 'opencv-python',
20-
# 'pytorch3d@https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py38_cu113_pyt1110/pytorch3d-0.7.2-cp38-cp38-linux_x86_64.whl',
21-
# 'jaxtyping',
22-
# 'scikit-image',
23-
# 'scikit-learn',
24-
# 'scipy',
2520
'oauthlib',
26-
# 'type_enforced',
2721
'spconv-cu117',
2822
'wandb',
2923
'cmapy',
3024
'pykeops',
31-
# 'seaborn',
3225
'plyfile',
3326
'py_structs'
3427
]
35-
dynamic = ["version"]
3628

3729
[tool.setuptools.packages]
3830
find = {} # Scan the project directory with the default parameters

smart_tree/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "1.0.0"

smart_tree/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
@hydra.main(
1515
version_base=None,
16-
config_path="conf",
16+
config_path=".conf",
1717
config_name="tree-dataset",
1818
)
1919
def main(cfg: DictConfig):
@@ -29,7 +29,7 @@ def main(cfg: DictConfig):
2929
pipeline.process_cloud(Path(f"{cfg.directory}/{p}"))
3030

3131
else:
32-
print("Please Supply a path or Directory")
32+
print("Please supply a path or directory to point clouds.")
3333

3434

3535
if __name__ == "__main__":

smart_tree/conf/tree-dataset.yaml

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ model_inference:
6767

6868
skeletonizer:
6969
K: 16
70-
min_connection_length: 0.00 #.02
70+
min_connection_length: 0.02
7171
minimum_graph_vertices: 32
72-
max_number_components: 30
72+
max_number_components: 8
7373
voxel_downsample: False
7474
edge_non_linear: None
7575

@@ -78,32 +78,37 @@ pipeline:
7878

7979
preprocessing:
8080
# Scale:
81-
# _target_: smart_tree.dataset.augmentations.Scale
82-
# min_scale: 1
83-
# max_scale: 1
81+
# _target_: smart_tree.dataset.augmentations.Scale
82+
# min_scale: 0.6
83+
# max_scale: 0.6
8484
# Scale:
8585
# _target_: smart_tree.dataset.augmentations.Scale
8686
# min_scale: 1
8787
# max_scale: 1
8888
# VoxelDownsample:
89-
# _target_: smart_tree.dataset.augmentations.VoxelDownsample
90-
# voxel_size : 0.01
89+
# _target_: smart_tree.dataset.augmentations.VoxelDownsample
90+
# voxel_size : 0.01
9191
#FixedRotate:
9292
# _target_: smart_tree.dataset.augmentations.FixedRotate
9393
# xyz: [0, 0, 90]
9494
# CentreCloud:
9595
# _target_: smart_tree.dataset.augmentations.CentreCloud
9696

9797

98-
repair_skeletons : True
99-
smooth_skeletons : True
100-
prune_skeletons : True
101-
min_skeleton_radius : 0.001 #0.005
102-
min_skeleton_length : 0.02
103-
view_model_output : False
98+
view_model_output : True
10499
view_skeletons : True
105100
save_outputs : False
106101
branch_classes: [0]
107102
cmap:
108-
- [1, 0, 0] # Trunk
109-
- [0, 1, 0] # Foliage
103+
- [0.325, 0.207, 0.039] # Trunk
104+
- [0.290, 0.703, 0.254] # Foliage
105+
106+
107+
108+
109+
repair_skeletons : True
110+
smooth_skeletons : True
111+
kernel_size: 10
112+
prune_skeletons : False
113+
min_skeleton_radius : 0.05 #0.005
114+
min_skeleton_length : 0.02

smart_tree/data_types/cloud.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def to_labelled_cld(self, radii, direction, class_l) -> LabelledCloud:
2727
return LabelledCloud(self.xyz, self.rgb, radii * direction, class_l)
2828

2929
def to_o3d_cld(self):
30-
return o3d_cloud(self.xyz, colours=self.rgb)
30+
cpu_cld = self.to_device("cpu")
31+
return o3d_cloud(cpu_cld.xyz, colours=cpu_cld.rgb)
3132

3233
def filter(self, mask):
3334
return Cloud(self.xyz[mask], self.rgb[mask])
@@ -50,8 +51,7 @@ def cat(self):
5051
)
5152

5253
def view(self):
53-
cpu_cld = self.to_device("cpu")
54-
o3d_viewer([cpu_cld.to_o3d_cld()])
54+
o3d_viewer([self.to_o3d_cld()])
5555

5656
def voxel_down_sample(self, voxel_size):
5757
idx = voxel_downsample(self.xyz, voxel_size)
@@ -111,6 +111,7 @@ def number_classes(self):
111111
@property
112112
def cmap(self):
113113
return torch.rand(self.number_classes, 3)
114+
<<<<<<< HEAD
114115

115116
def __add__(self, other):
116117
xyz = torch.cat((self.xyz, other.xyz))
@@ -119,6 +120,8 @@ def __add__(self, other):
119120
class_l = torch.cat((self.class_l, other.class_l))
120121

121122
return LabelledCloud(xyz, rgb, vector, class_l)
123+
=======
124+
>>>>>>> revert
122125

123126
def filter(self, mask):
124127
return LabelledCloud(
@@ -137,11 +140,7 @@ def filter_by_class(self, classes: List):
137140
return self.filter(mask)
138141

139142
def view(self, cmap=[]):
140-
if len(cmap) != 0:
141-
cmap = cmap
142-
else:
143-
cmap = self.cmap
144-
143+
cmap = cmap if cmap != [] else self.cmap
145144
cpu_cld = self.to_device("cpu")
146145
input_cld = cpu_cld.to_o3d_cld()
147146
segmented_cld = o3d_cloud(cpu_cld.xyz, colours=cmap[cpu_cld.class_l])
@@ -210,17 +209,8 @@ def medial_pts(self):
210209
@staticmethod
211210
def from_numpy(xyz, rgb, vector, class_l):
212211
return LabelledCloud(
213-
torch.from_numpy(xyz).float(), # -> these data types are stupid...
214-
torch.from_numpy(rgb).float(), # float64
215-
torch.from_numpy(vector).float(), # float32
216-
torch.from_numpy(class_l).int(), # int64
217-
)
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),
212+
torch.from_numpy(xyz), # float64 -> these data types are stupid...
213+
torch.from_numpy(rgb), # float64
214+
torch.from_numpy(vector), # float32
215+
torch.from_numpy(class_l), # int64
226216
)

smart_tree/data_types/tree.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ def prune(self, min_radius, min_length, root_id=0):
9393
continue
9494

9595
if branch.parent_id in branches_to_keep:
96-
if branch.length > min_length and branch.radii[0] > min_radius:
96+
if branch.length > min_length and (
97+
(branch.radii[0] > min_radius) or branch.radii[-1] > min_radius
98+
):
9799
branches_to_keep[branch_id] = branch
98100

99101
self.branches = branches_to_keep
@@ -107,8 +109,10 @@ def smooth(self, kernel_size=10):
107109
for branch in self.branches.values():
108110
if branch.radii.shape[0] >= kernel_size:
109111
branch.radii = np.convolve(
110-
branch.radii.ravel(), kernel, mode="same"
111-
).reshape(-1, 1)
112+
branch.radii.reshape(-1),
113+
kernel,
114+
mode="same",
115+
)
112116

113117

114118
@dataclass
@@ -123,9 +127,9 @@ def repair(self):
123127
for skeleton in self.skeletons:
124128
skeleton.repair()
125129

126-
def smooth(self):
130+
def smooth(self, kernel_size=10):
127131
for skeleton in self.skeletons:
128-
skeleton.smooth()
132+
skeleton.smooth(kernel_size=kernel_size)
129133

130134
def to_o3d_lineset(self):
131135
return o3d_merge_linesets(

smart_tree/dataset/dataset.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@ def compute_blocks(self):
155155

156156
self.block_centres = self.block_centres.to(torch.device("cpu"))
157157

158-
print("Blocks Computed")
159-
160158
def __getitem__(self, idx):
161159
block_centre = self.block_centres[idx]
162160
cloud: Cloud = self.clouds[idx]

smart_tree/model/model_inference.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,21 @@ def __init__(
4646
batch_size=4,
4747
use_colour=False,
4848
device=torch.device("cuda:0"),
49+
verbose=False,
4950
):
50-
print("Initalizing Model Inference")
5151
self.device = device
52-
self.model = load_model(model_path, weights_path, self.device)
53-
print("Model Loaded")
52+
self.verbose = verbose
5453
self.voxel_size = voxel_size
5554
self.block_size = block_size
5655
self.buffer_size = buffer_size
5756

5857
self.num_workers = num_workers
5958
self.batch_size = batch_size
6059

61-
self.num_input_feats = 6 if use_colour else 3
60+
self.model = load_model(model_path, weights_path, self.device)
61+
62+
if self.verbose:
63+
print("Model Loaded Succesfully")
6264

6365
def forward(self, cloud: Cloud, return_masked=True):
6466
outputs, inputs, masks = [], [], []

smart_tree/pipeline.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import numpy as np
44
import torch
5+
import time
56

7+
from copy import deepcopy
68
from .data_types.cloud import LabelledCloud, Cloud
79
from .data_types.tree import TreeSkeleton, DisjointTreeSkeleton
810
from hydra.utils import instantiate
@@ -41,7 +43,6 @@ def __init__(
4143
cmap=[[1, 0, 0], [0, 1, 0]],
4244
device=torch.device("cuda:0"),
4345
):
44-
print("Setting up pipeline...")
4546
self.inferer = inferer
4647
self.skeletonizer = skeletonizer
4748

@@ -67,19 +68,21 @@ def __init__(
6768

6869
def process_cloud(self, path: Path):
6970
# Load point cloud
70-
cloud: Cloud = load_cloud(path)
71+
cloud: Cloud = load_cloud(path).to_device(self.device)
7172
cloud = self.preprocessing(cloud)
7273

7374
# Run point cloud through model to predict class, radius, direction
74-
lc: LabelledCloud = self.inferer.forward(cloud).to_device("cuda")
75+
lc: LabelledCloud = self.inferer.forward(cloud).to_device(self.device)
7576
if self.view_model_output:
76-
lc.view(cmap=self.cmap)
77+
lc.view(self.cmap)
7778

7879
# Filter only the branch points for skeletonizaiton
7980
branch_cloud: LabelledCloud = lc.filter_by_class(self.branch_classes)
8081

8182
# Run the branch cloud through skeletonization algorithm, then post process
8283
skeleton: DisjointTreeSkeleton = self.skeletonizer.forward(branch_cloud)
84+
original_skeleton = deepcopy(skeleton)
85+
8386
self.post_process(skeleton)
8487

8588
# View skeletonization results
@@ -88,16 +91,17 @@ def process_cloud(self, path: Path):
8891
[
8992
skeleton.to_o3d_tube(),
9093
skeleton.to_o3d_lineset(),
91-
cloud.to_o3d_cld(),
9294
skeleton.to_o3d_tube(colour=False),
95+
cloud.to_o3d_cld(),
96+
original_skeleton.to_o3d_tube(),
9397
],
9498
line_width=5,
9599
)
96100

97101
if self.save_outputs:
98-
save_o3d_mesh("skeleton.ply", skeleton.to_o3d_lineset())
99-
save_o3d_lineset("mesh.ply", skeleton.to_o3d_tube())
100-
save_o3d_cloud("mesh.ply", cloud.to_o3d_cld())
102+
save_o3d_lineset("skeleton.ply", skeleton.to_o3d_lineset())
103+
save_o3d_mesh("mesh.ply", skeleton.to_o3d_tube())
104+
save_o3d_cloud("cloud.ply", cloud.to_o3d_cld())
101105

102106
def post_process(self, skeleton: DisjointTreeSkeleton):
103107
if self.prune_skeletons:
@@ -110,8 +114,7 @@ def post_process(self, skeleton: DisjointTreeSkeleton):
110114
skeleton.repair()
111115

112116
if self.smooth_skeletons:
113-
print("Smoothing...")
114-
skeleton.smooth()
117+
skeleton.smooth(kernel_size=30)
115118

116119
@staticmethod
117120
def from_cfg(inferer, skeletonizer, cfg):

0 commit comments

Comments
 (0)