Example of sulcal graph in slam

# Authors:
# Lucile Hashimoto lucile-hashimoto
# Guillaume Auzias <guillaume.auzias@univ-amu.fr>

# License: MIT
# sphinx_gallery_thumbnail_number = 2

importation of slam modules

import numpy as np
import networkx as nx
import slam.io as sio
import slam.sulcal_graph as ssg
import slam.watershed as swat
import slam.remeshing as srem

loading an examplar mesh and corresponding texture

path_to_mesh = "../examples/data/example_mesh.gii"
path_to_texture = "../examples/data/example_texture.gii"
path_to_mask = None
mesh = sio.load_mesh(path_to_mesh)
side = "left"
texture = sio.load_texture(path_to_texture)
dpf = np.array(texture.darray[0])

# define the exclusion mask (cingular pole)
if path_to_mask is not None:
    mask = sio.load_texture(path_to_mask).darray[0]
else:
    mask = None

thresh_dist_perc=0
thresh_ridge=0
thresh_area_perc=0

extract the sulcal graph from a mesh

        Computing the curvature

Calculating vertex normals .... Please wait
Finished calculating vertex normals
Calculating curvature tensors ... Please wait
Finished Calculating curvature tensors
Calculating Principal Components ... Please wait
Finished Calculating principal components

        Computing the DPF

  Computing Laplacian
    Computing mesh weights of type conformal
    -edge length threshold needed for  0  values =  0.0  %
    -number of Nan in weights:  0  =  0.0  %
    -number of Negative values in weights:  936  =  6.706792777300086  %
    -nb Nan in Laplacian :  0
    -nb Inf in Laplacian :  0

        Computing Voronoi's vertex

    -percent polygon with obtuse angle  29.4067067927773
Computing watershed by flooding...
Thresholds provided:
-Distance between 2 pits: 0 % <=> 0.0 mm
-Ridge height: 0
-Basin area: 0 % <=> 0.0 mm2

add an attribute to nodes

g = ssg.add_node_attribute_from_texture(g, dpf, attribute_name='pit_depth')
print("Node attributes:\n", g.nodes[0].keys())
print("First node:\n", g.nodes[0])
Node attributes:
 dict_keys(['pit_index', 'basin_vertices', 'pit_depth', 'basin_area', 'basin_label'])
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float32(-0.050967123), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0}

add an attribute to edges

g = ssg.add_edge_attribute_from_texture(g, dpf, attribute_name='ridge_depth_bis')
print("Edge attributes:\n", g.edges[list(g.edges)[0]].keys())
print("First edge:\n", g.edges[list(g.edges)[0]])
Edge attributes:
 dict_keys(['weight', 'ridge_vertices', 'ridge_length', 'ridge_index', 'ridge_depth', 'ridge_depth_diff_min', 'ridge_depth_diff_max', 'ridge_depth_bis'])
First edge:
 {'weight': 1, 'ridge_vertices': array([ 444,  449,  480, 1997, 2045]), 'ridge_length': 5, 'ridge_index': np.int64(2045), 'ridge_depth': np.float64(-0.005626444880465831), 'ridge_depth_diff_min': np.float64(0.0022356885067722687), 'ridge_depth_diff_max': np.float64(0.007418748606375233), 'ridge_depth_bis': -0.01064499281346798}

add geodesic distances attribute to edges

g = ssg.add_geodesic_distances_to_edges(g, mesh)

add mean value to nodes attributes

g = ssg.add_mean_value_to_nodes(g, dpf, attribute_name='basin_mean_depth')

get textures from graph

atex_labels, atex_pits, atex_ridges = ssg.get_textures_from_graph(g, mesh)
A more detailed computation of the sulcal graph

with explicit call to the watershed

mean_curvature, dpf, voronoi = swat.compute_mesh_features(mesh)
        Computing the curvature

Calculating vertex normals .... Please wait
Finished calculating vertex normals
Calculating curvature tensors ... Please wait
Finished Calculating curvature tensors
Calculating Principal Components ... Please wait
Finished Calculating principal components

        Computing the DPF

  Computing Laplacian
    Computing mesh weights of type conformal
    -edge length threshold needed for  0  values =  0.0  %
    -number of Nan in weights:  0  =  0.0  %
    -number of Negative values in weights:  936  =  6.706792777300086  %
    -nb Nan in Laplacian :  0
    -nb Inf in Laplacian :  0

        Computing Voronoi's vertex

    -percent polygon with obtuse angle  29.4067067927773

extract sulcal pits and associated basins

basins, ridges, adjacency = swat.watershed(
    mesh, voronoi, dpf, thresh_dist_perc, thresh_ridge, thresh_area_perc, mask)

# generate the sulcal graph
g = ssg.get_sulcal_graph(adjacency, basins, ridges)
Computing watershed by flooding...
Thresholds provided:
-Distance between 2 pits: 0 % <=> 0.0 mm
-Ridge height: 0
-Basin area: 0 % <=> 0.0 mm2

generate the textures from graph

atex_labels_graph, atex_pits_graph, atex_ridges_graph = (ssg.get_textures_from_graph
                                    (g, mesh))
# compare the textures extracted from the watershed with the ones extarcted from the graph
# they should be identical
print("vertex-to-vertex difference between the texture extracted from the "
      "watershed and the one extracted from the graph, should be strictly qual to 0")
print(np.max(atex_ridges-atex_ridges_graph))
vertex-to-vertex difference between the texture extracted from the watershed and the one extracted from the graph, should be strictly qual to 0
0.0

add as a new node attribute the 3D coordinates of the vertex corresponding to the pit in the mesh used to compute the watershed

g = ssg.add_coords_attribute(g, mesh,
                                    attribute_vert_index='pit_index',
                                    new_attribute_key='3dcoords')
print("First node:\n", g.nodes[0])
{'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ])}
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ])}

add as a new node attribute the 3D coordinates of the vertex corresponding to the pit in the spherical mesh obtained from the original mesh, so that ‘pit_index’ also gives the corresponding vertex in that mesh

source_spherical_mesh_file = "../examples/data/example_mesh_spherical.gii"
source_spherical_mesh = sio.load_mesh(source_spherical_mesh_file)

g = ssg.add_coords_attribute(g, source_spherical_mesh,
                                    attribute_vert_index='pit_index',
                                    new_attribute_key='sphere_3dcoords')
print("First node:\n", g.nodes[0])
{'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251])}
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251])}

Load another mesh and corresponding spherical version to be used as a target onto which the graph will be projected

target_mesh_file = "../examples/data/example_mesh_2.gii"
target_mesh = sio.load_mesh(target_mesh_file)
target_spherical_mesh_file = "../examples/data/example_mesh_2_spherical.gii"
target_spherical_mesh = sio.load_mesh(target_spherical_mesh_file)

Project the depth texture using resampling.texture_spherical_interpolation_nearest_neighbor for the visu

interpolated_dpf = srem.texture_spherical_interpolation_nearest_neighbor(
    source_spherical_mesh, target_spherical_mesh, dpf)

Compute the ‘interpolated_pits_index’ corresponding to the index of the nearest neighbor of each pit in the target spherical mesh

g = ssg.vertex_index_interpolation(g, target_spherical_mesh,
                                              graph_spherical_coords_attribute='sphere_3dcoords',
                                              interpolated_attribute='interpolated_pits_index')
print("First node:\n", g.nodes[0])
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251]), 'interpolated_pits_index': 492}

Here is the way to get the list of ‘interpolated_pits_index’ for all nodes

interp_pits_inds = np.array(list(nx.get_node_attributes(g, 'interpolated_pits_index').values()))

add as a new node attribute the 3D coordinates of the vertex corresponding to # the inperpolated pit in the target spherical mesh

g = ssg.add_coords_attribute(g, target_spherical_mesh,
                                    attribute_vert_index='interpolated_pits_index',
                                    new_attribute_key='target_sphere_3dcoords')
print("First node:\n", g.nodes[0])
{'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251]), 'interpolated_pits_index': 492, 'target_sphere_3dcoords': array([0.45437539, 0.89028907, 0.03046896])}
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251]), 'interpolated_pits_index': 492, 'target_sphere_3dcoords': array([0.45437539, 0.89028907, 0.03046896])}

add as a new node attribute the 3D coordinates of the vertex corresponding to # the inperpolated pit in the target mesh

g = ssg.add_coords_attribute(g, target_mesh,
                                    attribute_vert_index='interpolated_pits_index',
                                    new_attribute_key='target_mesh_3dcoords')
print("First node:\n", g.nodes[0])
{'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251]), 'interpolated_pits_index': 492, 'target_sphere_3dcoords': array([0.45437539, 0.89028907, 0.03046896]), 'target_mesh_3dcoords': array([69.98622894, 68.32143402, 50.87675095])}
First node:
 {'pit_index': 530, 'basin_vertices': [530, 535, 2172, 529, 524, 2131, 2173, 531, 947, 964, 941, 911, 2199, 2174, 559, 912, 2132, 2187, 2198, 910, 909, 2158, 546, 2091, 561, 513, 2074, 2200, 871, 948, 537, 927, 860, 2211, 2133, 2188, 894, 861, 895, 862, 2216, 863, 875, 846, 966, 2204, 2212, 2104, 496, 873, 514, 913, 2038, 560, 2218, 847, 2134, 819, 876, 965, 2135, 2159, 2213, 516, 967, 949, 462, 874, 503, 548, 794, 2161, 2138, 2215, 2136, 2205, 463, 497, 968, 806, 782, 929, 884, 2163, 2219, 356, 2217, 831, 486, 464, 2220, 2227, 450, 155, 553, 2214, 2230, 848, 440, 952, 795, 456, 2177, 2190, 878, 741, 2232, 2231, 781, 1925, 774, 547, 896, 2242, 747, 2191, 808, 2221, 914, 942, 566, 885, 2250, 536, 2264, 2235, 769, 797, 850, 915, 2251, 552, 578, 515, 2265, 743, 879, 851, 2233, 886, 2266, 621, 770, 596, 2234, 587, 880, 488, 605, 562, 615, 2222, 579, 809, 852, 629, 853, 604, 2244, 640, 489, 748, 2252, 2236, 641, 458, 2223, 2243, 639, 608, 2267, 2268, 427, 2280, 403, 411, 393, 382, 370, 1529], 'pit_depth': np.float64(-0.013045193486841064), 'basin_area': np.float64(445.9305499974789), 'basin_label': 0, '3dcoords': array([ 6.71122503, -2.44120479,  0.5532046 ]), 'sphere_3dcoords': array([ 0.46400985,  0.88582945, -0.00102251]), 'interpolated_pits_index': 492, 'target_sphere_3dcoords': array([0.45437539, 0.89028907, 0.03046896]), 'target_mesh_3dcoords': array([69.98622894, 68.32143402, 50.87675095])}

VISUALIZATION USING plotly

import slam.plot as splt

mesh_data = {
    "vertices": mesh.vertices,
    "faces": mesh.faces,
    "opacity": 0.5,
    "title": 'Source'
}
intensity_data = {
    "values": dpf,
    "mode": "vertex",
}
fig1 = splt.plot_mesh(
    mesh_data=mesh_data,
    intensity_data=intensity_data)
fig1 = splt.plot_graph(g, coords_attribute='3dcoords', fig=fig1,
        marker={"size": 6, "color": "white", "line":{"color":"black", "width":2}},
)
fig1.show()
fig1


mesh_data = {
    "vertices": source_spherical_mesh.vertices,
    "faces": source_spherical_mesh.faces,
    "title": 'Source spherical'
}
fig2 = splt.plot_mesh(
    mesh_data=mesh_data,
    intensity_data=intensity_data)
# add the pits to the plot
pits_coords = np.array(list(nx.get_node_attributes(g, 'sphere_3dcoords').values()))
trace_points = splt.plot_points(
    pits_coords,
    marker={"size": 6, "color": "white", "line":{"color":"black", "width":2}},
)
fig2.add_trace(trace_points)
fig2.show()
fig2


mesh_data = {
    "vertices": target_spherical_mesh.vertices,
    "faces": target_spherical_mesh.faces,
    "title": 'Target sphere'
}
intensity_data = {
    "values": interpolated_dpf,
    "mode": "vertex",
}
fig3 = splt.plot_mesh(
    mesh_data=mesh_data,
    intensity_data=intensity_data)
# add the pits to the plot
pits_init = splt.plot_points(
    pits_coords,
    marker={"size": 6, "color": "white", "line":{"color":"black", "width":2}},
)
fig3.add_trace(pits_init)
# add the interpolated pits to the plot
interp_pits_coords_sphere = np.array(list(nx.get_node_attributes(g, 'target_sphere_3dcoords').values()))
pits_interpolated = splt.plot_points(
    interp_pits_coords_sphere,
    marker={"size": 6, "color": "red", "line":{"color":"black", "width":2}},
)
fig3.add_trace(pits_interpolated)
fig3.show()
fig3


mesh_data = {
    "vertices": target_mesh.vertices,
    "faces": target_mesh.faces,
    "title": 'Target mesh'
}
intensity_data = {
    "values": interpolated_dpf,
    "mode": "vertex",
}
fig4 = splt.plot_mesh(
    mesh_data=mesh_data,
    intensity_data=intensity_data)
# add the interpolated pits to the plot
interp_pits_coords_mesh = np.array(list(nx.get_node_attributes(g, 'target_mesh_3dcoords').values()))
pits_interpolated = splt.plot_points(
    interp_pits_coords_mesh,
    marker={"size": 6, "color": "red", "line":{"color":"black", "width":2}}
)
fig4.add_trace(pits_interpolated)
fig4.show()
fig4


Total running time of the script: (0 minutes 18.263 seconds)

Gallery generated by Sphinx-Gallery