Skip to content

Commit 06d827f

Browse files
feat: add attribute import based on vertex matching
1 parent dcf2588 commit 06d827f

File tree

1 file changed

+75
-2
lines changed

1 file changed

+75
-2
lines changed

osteoid/skeleton.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,13 +1392,86 @@ def chunk(
13921392
origin[0], origin[1], origin[2],
13931393
)
13941394
del vertices
1395-
1395+
13961396
skel_chunks = {}
13971397
for grid, (verts, edges) in chunks.items():
1398-
skel_chunks[grid] = Skeleton(verts, edges).consolidate()
1398+
sk = Skeleton(verts, edges).consolidate()
1399+
sk.import_attributes(self)
1400+
skel_chunks[grid] = sk
13991401

14001402
return skel_chunks
14011403

1404+
def import_attributes(self, other:"Skeleton"):
1405+
"""
1406+
Copy the attributes of another skeleton
1407+
for matching vertices.
1408+
1409+
Vertices are considered matching if they have the same coordinates
1410+
(within floating point tolerance).
1411+
1412+
Assumes importing skeleton has no extra attributes initially,
1413+
only vertices and edges.
1414+
1415+
Parameters:
1416+
-----------
1417+
other : Skeleton
1418+
The skeleton to copy attributes from
1419+
"""
1420+
if self.vertices.shape[0] == 0 or other.vertices.shape[0] == 0:
1421+
return
1422+
1423+
EPSILON = 1e-7
1424+
1425+
self_rounded = np.round(self.vertices / EPSILON).astype(np.int64)
1426+
other_rounded = np.round(other.vertices / EPSILON).astype(np.int64)
1427+
1428+
# Use lexsort for efficient matching
1429+
# Sort both arrays and find matching indices
1430+
self_sort_idx = np.lexsort(self_rounded.T)
1431+
other_sort_idx = np.lexsort(other_rounded.T)
1432+
1433+
self_sorted = self_rounded[self_sort_idx]
1434+
other_sorted = other_rounded[other_sort_idx]
1435+
1436+
# Find matches between sorted arrays
1437+
i, j = 0, 0
1438+
matches = [] # (self_idx, other_idx) pairs
1439+
1440+
while i < len(self_sorted) and j < len(other_sorted):
1441+
cmp = np.sum((self_sorted[i] - other_sorted[j]) != 0)
1442+
1443+
if cmp == 0: # Match found
1444+
matches.append((self_sort_idx[i], other_sort_idx[j]))
1445+
i += 1
1446+
j += 1
1447+
elif np.any(self_sorted[i] < other_sorted[j]):
1448+
i += 1
1449+
else:
1450+
j += 1
1451+
1452+
if len(matches) == 0:
1453+
return
1454+
1455+
matches = np.array(matches, dtype=np.uint32)
1456+
self_indices = matches[:, 0]
1457+
other_indices = matches[:, 1]
1458+
1459+
# Copy attribute data for matching vertices
1460+
for attr in other.extra_attributes:
1461+
attr_name = attr['id']
1462+
other_buf = getattr(other, attr_name)
1463+
1464+
if other_buf.ndim == 1:
1465+
self_buf = np.zeros(self.vertices.shape[0], dtype=other_buf.dtype)
1466+
else:
1467+
self_buf = np.zeros(
1468+
(self.vertices.shape[0], other_buf.shape[1]),
1469+
dtype=other_buf.dtype
1470+
)
1471+
1472+
self_buf[self_indices] = other_buf[other_indices]
1473+
self.add_vertex_attribute(attr_name, self_buf)
1474+
14021475
def __str__(self):
14031476
template = "{}=({}, {})"
14041477
attr_strings = []

0 commit comments

Comments
 (0)