Beispiel #1
0
def get_stent_likely_positions(data, th):
    """ Get a pointset of positions that are likely to be on the stent.
    If the given data has a "sampling" attribute, the positions are
    scaled accordingly. 
    
    Detection goes according to three criteria:
      * intensity above given threshold
      * local maximum
      * at least one neighbour with intensity above threshold
    """

    # Get mask
    mask = get_mask_with_stent_likely_positions(data, th)

    # Convert mask to points
    indices = np.where(mask == 2)  # Tuple of 1D arrays
    pp = PointSet(np.column_stack(reversed(indices)), dtype=np.float32)

    # Correct for anisotropy and offset
    if hasattr(data, 'sampling'):
        pp *= PointSet(list(reversed(data.sampling)))
    if hasattr(data, 'origin'):
        pp += PointSet(list(reversed(data.origin)))

    return pp
Beispiel #2
0
def pp_to_graph(pp, type='oneEdge'):
    """ PointSet to graph with points connected with edges or as one edge.
    Returns graph. Can be used for centerline output.
    """
    from stentseg.stentdirect import stentgraph
    graph = stentgraph.StentGraph()
    if type == 'oneEdge':
        # add single nodes
        for p in pp:
            p_as_tuple = tuple(p.flat)
            graph.add_node(p_as_tuple)
        # add one path of pp
        pstart = p_as_tuple
        pend = tuple(pp[-1].flat)
        graph.add_edge(pstart, pend, path=pp)
    else:
        for i, p in enumerate(pp[:-1]):
            n1 = tuple(p.flat)
            n2 = tuple(pp[i + 1].flat)
            # create path between nodes as PointSet
            path = PointSet(3, dtype=np.float32)
            for p in [n1, n2]:
                path.append(p)
            graph.add_edge(n1, n2, path=path)

    return graph
Beispiel #3
0
def plot_points(pp, mc='g', ms='o', mw=8, alpha=0.5, ls='', ax=None, **kwargs):
    """ Plot a point or set of points in current axis and restore current view 
    alpha 0.9 = solid; 0.1 transparant
    """
    if ax is None:
        ax = vv.gca()
    # check if pp is 1 point and not a PointSet
    if not isinstance(pp, PointSet):
        pp = np.asarray(pp)
        if pp.ndim == 1:
            p = PointSet(3)
            p.append(pp)
            pp = p
    # get view and plot
    view = ax.GetView()
    point = vv.plot(PointSet(pp),
                    mc=mc,
                    ms=ms,
                    mw=mw,
                    ls=ls,
                    alpha=alpha,
                    axes=ax,
                    **kwargs)
    ax.SetView(view)

    return point
Beispiel #4
0
def get_stent_orientation(R1, R2):
    R1, R2 = np.asarray(R1), np.asarray(R2)
    R1, R2 = PointSet(R1), PointSet(R2)  # turn array ndim2 into PointSet
    R1_ant, R1_post, R1_left, R1_right = R1[0], R1[1], R1[2], R1[3]
    R2_ant, R2_post, R2_left, R2_right = R2[0], R2[1], R2[2], R2[3]
    refvector = [0, 0, 10]  # z-axis
    angle = (R1_ant - R2_ant).angle(refvector)  # order does not matter
Beispiel #5
0
def convert_paths_to_PointSet(g):
    """ for centerline graphs that were created with paths as list
    """
    from stentseg.utils import PointSet
    import numpy as np
    for n1, n2 in g.edges():
        path2 = PointSet(3, dtype=np.float32)
        # Obtain path of edge
        path = g.edge[n1][n2]['path']
        for p in path:
            path2.append(tuple(p.flat))
        g.edge[n1][n2]['path'] = path2.copy()
Beispiel #6
0
def point_in_pointcloud_closest_to_p(pp, point):
    """ Find point in PointSet which is closest to a point
    Returns a PointSet with point found in PointSet and point 
    """
    vecs = pp - point
    dists_to_pp = ((vecs[:, 0]**2 + vecs[:, 1]**2 +
                    vecs[:, 2]**2)**0.5).reshape(-1, 1)
    pp_index = list(dists_to_pp).index(dists_to_pp.min())  # index on path
    pp_point = pp[pp_index]
    p_in_pp_and_point = PointSet(3, dtype='float32')
    [p_in_pp_and_point.append(*p) for p in (pp_point, point)]

    return p_in_pp_and_point
Beispiel #7
0
def _find_better_pos3(pp, pos, vec, step, n):
    """ In 3D we search recursively left/right, forward/backward.
    Maybe this implementation could instead do a circle fit.
    """

    stub1, stub2 = PointSet([1, 1, 1]), PointSet([1, 0, 1])
    v1 = vec.cross(stub1)
    v1 = v1 if v1.norm() > 0 else vec.cross(stub2)
    v2 = vec.cross(v1)
    v1, v2 = v1.normalize() * step, v2.normalize() * step

    measure = _distance_measure(pp, pos, n)

    # for loops are like while loops with a failsafe

    for i in range(5):

        curpos = pos

        for i in range(30):
            new_pos = pos + v1
            new_measure = _distance_measure(pp, new_pos, n)
            if new_measure <= measure:
                break
            pos, measure = new_pos, new_measure

        for i in range(30):
            new_pos = pos - v1
            new_measure = _distance_measure(pp, new_pos, n)
            if new_measure <= measure:
                break
            pos, measure = new_pos, new_measure

        for i in range(30):
            new_pos = pos + v2
            new_measure = _distance_measure(pp, new_pos, n)
            if new_measure <= measure:
                break
            pos, measure = new_pos, new_measure

        for i in range(30):
            new_pos = pos - v2
            new_measure = _distance_measure(pp, new_pos, n)
            if new_measure <= measure:
                break
            pos, measure = new_pos, new_measure

        if (pos == curpos).all():
            break

    return pos
Beispiel #8
0
def get_picked_seed(data, label):
    """ Picked point (SHIFT+r-click) is converted to world coordinates as in Step1
    Input: volume data, label
    """
    from stentseg.utils import PointSet

    coord = label2volindices(label)  # [x,y,z]
    p = PointSet(coord, dtype=np.float32)
    # Correct for anisotropy and offset
    if hasattr(data, 'sampling'):
        p *= PointSet(list(reversed(data.sampling)))
    if hasattr(data, 'origin'):
        p += PointSet(list(reversed(data.origin)))
    return list(p.flat)
Beispiel #9
0
def points_from_nodes_in_graph(graph):
    """
    """
    # Create set of tuples to remove duplicates
    pp = set(tuple(p) for p in graph.nodes())
    # Turn into a pointset
    return PointSet(np.array(list(pp)))
Beispiel #10
0
def measure_centerline_strain():
    """ Measure the centerline strain.
    """
    i1 = slider_ref.value
    i2 = slider_ves.value

    # Get the centerline section of interest
    index1 = int(np.ceil(i1))
    index2 = int(np.floor(i2))
    section = centerline[index1:index2 + 1]

    # get this section of the centerline for each phase
    sections = []
    for phase in range(len(deforms)):
        deform = deforms[phase]
        dx = deform.get_field_in_points(section, 0)
        dy = deform.get_field_in_points(section, 1)
        dz = deform.get_field_in_points(section, 2)
        deform_vectors = PointSet(np.stack([dx, dy, dz], 1))
        sections.append(section + deform_vectors)

    # Measure the strain of the full section, by measuring the total length in each phase.
    lengths = []
    for phase in range(len(deforms)):
        section = sections[phase]
        length = sum(
            float(section[i].distance(section[i + 1]))
            for i in range(len(section) - 1))
        lengths.append(length)

    if min(lengths) == 0:
        return 0
    else:
        # Strain as delta-length divided by initial length
        return (max(lengths) - min(lengths)) / min(lengths)
Beispiel #11
0
def get_midpoints_peaksvalleys(model):
    """ Get midpoints near the peaks and valleys
    """
    # remove hooks, pop nodes
    model_hooks1, model_noHooks1 = _get_model_hooks(model)
    stentgraph.pop_nodes(model_noHooks1)
    midpoints_peaks_valleys = PointSet(3)
    for n1, n2 in sorted(model_noHooks1.edges()):
        if stentgraph._edge_length(model_noHooks1, n1, n2) < 10:  # in mm
            # get midpoint for edges near struts
            mid = (n1[0] + n2[0]) / 2, (n1[1] + n2[1]) / 2, (n1[2] + n2[2]) / 2
            path = model_noHooks1.edge[n1][n2]['path']
            mid_and_pathpoint = point_in_pointcloud_closest_to_p(path, mid)
            midpoints_peaks_valleys.append(
                mid_and_pathpoint[0])  # append pp_point
    return midpoints_peaks_valleys
Beispiel #12
0
def points_from_mesh(mesh, invertZ=True):
    """ Create a point cloud (represented as a PointSet) from a visvis mesh
    object, or from a filename pointing to a .stl or .obj file.
    """
    if isinstance(mesh, str):
        mesh = vv.meshRead(mesh)
    if invertZ == True:
        for vertice in mesh._vertices:
            vertice[-1] = vertice[-1] * -1
    # Create set of tuples to remove duplicates
    pp = set(tuple(p) for p in mesh._vertices)
    # Turn into a pointset
    return PointSet(np.array(list(pp)))
Beispiel #13
0
def points_from_edges_in_graph(graph, type='order'):
    """ Get all points on the paths in the graph, return as list with
    PointSets per path. If type='order' return in correct order of path points;
    there may be duplicates between edges, not within a selfloop edge.
    If type = 'noduplicates' return in random order but without duplicate points.
    """
    paths_as_pp = []
    for n1, n2 in graph.edges():
        path = graph.edge[n1][n2]['path']  # points on path include n1 n2
        if type == 'noduplicates':
            # Create set of tuples to remove duplicates; but order is lost!
            path = np.asarray(path)
            pp = set(tuple(p) for p in path)
            # turn into a pointset
            path_as_pp = PointSet(np.array(list(pp)))
        elif type == 'order':
            if path[0].all() == path[-1].all():
                path = path[1:]  # remove the duplicate (in selfloop)
                path_as_pp = PointSet(path)
        paths_as_pp.append(path_as_pp)

    return paths_as_pp
Beispiel #14
0
def get_isosurface3d(vol, threshold=300, showsurface=False):
    """
    Generate isosurface to return the vertices (surface_points), based on HU threshold
    Vertices are scaled using vol.sampling and vol.origin
    return: verts, faces, pp_verts, mesh
    """
    from skimage import measure, morphology
    from mpl_toolkits.mplot3d.art3d import Poly3DCollection

    verts, faces = measure.marching_cubes(vol, threshold)  # verts in z,y,x
    #verts, faces, normals, values = measure.marching_cubes_lewiner(vol,level=threshold)
    #version after 0.12 returns verts, faces, normals, values

    vertpoints = copy.deepcopy(verts)
    # set scale and origin
    vertpoints[:, 0] *= vol.sampling[0]  # sampling z,y,x
    vertpoints[:, 1] *= vol.sampling[1]
    vertpoints[:, 2] *= vol.sampling[2]
    vertpoints[:, 0] += vol.origin[0]
    vertpoints[:, 1] += vol.origin[1]
    vertpoints[:, 2] += vol.origin[2]

    pp = vertpoints.copy()  # pp are the surfacepoints
    pp[:, 0] = vertpoints[:, -1]  # to x y x
    pp[:, -1] = vertpoints[:, 0]  # to x y z

    verts = copy.deepcopy(
        pp)  # vertice points xyz scaled and set to vol origin
    pp_verts = PointSet(pp)

    if showsurface:
        # Fancy indexing: `verts[faces]` to generate a collection of triangles
        mesh = Poly3DCollection(verts[faces], alpha=0.1)
        face_color = [0, 0, 1]
        mesh.set_facecolor(face_color)

        fig = plt.figure(figsize=(15, 15))
        ax = fig.add_subplot(111, projection='3d')

        ax.add_collection3d(mesh)

        ax.set_xlim(0, vol.shape[0])
        ax.set_ylim(0, vol.shape[1])
        ax.set_zlim(0, vol.shape[2])

        plt.show()

    else:
        mesh = None

    return verts, faces, pp_verts, mesh
Beispiel #15
0
def get_vessel_points_from_plane_points(pp):
    """ Select points from the vessel points that are very close to the plane
    defined by the given plane points. Returns a 2D and a 3D point set.
    """
    abcd = fitting.fit_plane(pp)

    # Get 2d and 3d coordinates of points that lie (almost) on the plane
    # pp2 = fitting.project_to_plane(ppvessel, abcd)
    # pp3 = fitting.project_from_plane(pp2, abcd)
    signed_distances = fitting.signed_distance_to_plane(ppvessel, abcd)
    distances = np.abs(signed_distances)

    # Select points to consider. This is just to reduce the search space somewhat.
    selection = np.where(distances < 5)[0]

    # We assume that each tree points in ppvessel makes up a triangle (face)
    # We make sure of that when we load the mesh.
    # Select first index of each face (every 3 vertices is 1 face), and remove duplicates
    selection_faces = set(3 * (selection // 3))

    # Now iterate over the faces (triangles), and check each edge. If the two
    # points are on different sides of the plane, then we interpolate on the
    # edge to get the exact spot where the edge intersects the plane.
    sampled_pp3 = PointSet(3)
    visited_edges = set()
    for fi in selection_faces:  # for each face index
        for edge in [(fi + 0, fi + 1), (fi + 0, fi + 2), (fi + 1, fi + 2)]:
            if signed_distances[edge[0]] * signed_distances[edge[1]] < 0:
                if edge not in visited_edges:
                    visited_edges.add(edge)
                    d1, d2 = distances[edge[0]], distances[edge[1]]
                    w1, w2 = d2 / (d1 + d2), d1 / (d1 + d2)
                    p = w1 * ppvessel[edge[0]] + w2 * ppvessel[edge[1]]
                    sampled_pp3.append(p)

    return fitting.project_to_plane(sampled_pp3, abcd), sampled_pp3
Beispiel #16
0
def get_plane_points_from_centerline_index(i):
    """ Get a set of points that lie on the plane orthogonal to the centerline
    at the given index. The points are such that they can be drawn as a line for
    visualization purposes. The plane equation can be obtained via a plane-fit.
    """

    if True:
        # Cubic fit of the centerline

        i = max(1.1, min(i, centerline.shape[0] - 2.11))
        # Sample center point and two points right below/above, using
        # "cardinal" interpolating (C1-continuous), or "basic" approximating (C2-continious).
        pp = []
        for j in [i - 0.1, i, i + 0.1]:
            index = int(j)
            t = j - index
            coefs = pirt.interp.get_cubic_spline_coefs(t, "basic")
            samples = centerline[index - 1], centerline[index], centerline[
                index + 1], centerline[index + 2]
            pp.append(samples[0] * coefs[0] + samples[1] * coefs[1] +
                      samples[2] * coefs[2] + samples[3] * coefs[3])
        # Get center point and vector pointing down the centerline
        p = pp[1]
        vec1 = (pp[2] - pp[1]).normalize()

    else:
        # Linear fit of the centerline

        i = max(0, min(i, centerline.shape[0] - 2))
        index = int(i)
        t = i - index
        # Sample two points of interest
        pa, pb = centerline[index], centerline[index + 1]
        # Get center point and vector pointing down the centerline
        p = t * pb + (1 - t) * pa
        vec1 = (pb - pa).normalize()

    # Get two orthogonal vectors that define the plane that is orthogonal
    # to the above vector. We can use an arbitrary vector to get the first,
    # but there is a tiiiiiny chance that it is equal to vec1 so that the
    # normal collapese.
    vec2 = vec1.cross([0, 1, 0])
    if vec2.norm() == 0:
        vec2 = vec1.cross((1, 0, 0))
    vec3 = vec1.cross(vec2)

    # Sample some point on the plane and get the plane's equation
    pp = PointSet(3)
    radius = 6
    pp.append(p)
    for t in np.linspace(0, 2 * np.pi, 12):
        pp.append(p + np.sin(t) * radius * vec2 + np.cos(t) * radius * vec3)
    return pp
Beispiel #17
0
def deform_points_2d(pp2, plane):
    """ Given a 2D pointset (and the plane that they are on),
    return a list with the deformed versions of that pointset.
    """
    pp3 = fitting.project_from_plane(
        pp2, plane)  #todo: shouldn't origin be subtracted?! see dynamic.py
    deformed = []
    for phase in range(len(deforms)):
        deform = deforms[phase]
        dx = deform.get_field_in_points(
            pp3, 0
        )  #todo: shouldn't this be z=0 y=1 x=2! see dynamic.py; adapt in all functions?!
        dy = deform.get_field_in_points(pp3, 1)
        dz = deform.get_field_in_points(pp3, 2)
        deform_vectors = PointSet(np.stack([dx, dy, dz], 1))
        pp3_deformed = pp3 + deform_vectors
        deformed.append(fitting.project_to_plane(pp3_deformed, plane))
    return deformed
Beispiel #18
0
def on_key(event):
    if event.key == vv.KEY_CONTROL:
        global nr
        coordinates = np.asarray(label2worldcoordinates(label),
                                 dtype=np.float32)  # x,y,z
        n2 = tuple(coordinates.flat)
        sd2._nodes1.add_node(n2, number=nr)
        print(nr)
        if nr > 0:
            for n in list(sd2._nodes1.nodes()):
                if sd2._nodes1.node[n]['number'] == nr - 1:
                    path = [n2, n]
                    sd2._nodes1.add_edge(n2,
                                         n,
                                         path=PointSet(np.row_stack(path)))
        sd2._nodes1.Draw(mc='r', mw=10, lc='y')
        nr += 1
    if event.key == vv.KEY_ENTER:
        sd2._graphrefined = sd2._RefinePositions(sd2._nodes1)
        sd2._graphrefined.Draw(mc='b', mw=10, lc='g')
    if event.key == vv.KEY_ESCAPE:
        # Store to EXCEL
        pp1 = []
        try:
            pp1 = sd2._graphrefined.nodes()
            pp1.sort(key=lambda x: sd2._graphrefined.node[x]['number']
                     )  # sort nodes by click number
            # pp1 = sd2._graphrefined.nodes()
            print('*** refined manual picked were stored ***')
        except AttributeError:
            pp1 = sd2._nodes1.nodes()
            pp1.sort(key=lambda x: sd2._nodes1.node[x]['number']
                     )  # sort nodes by click number
            print('*** manual picked were stored ***')
        pp1 = np.asarray(pp1)
        storeCoordinatesToExcel(pp1, exceldir)
        print('---stored to excel {} ---'.format(exceldir))
        print('---model can be stored as ssdf in do_segmentation---')
Beispiel #19
0
                                  regsteps=params.regsteps,
                                  verbose=False)

# do not use first point, as they are influenced by user selected points
centerline1 = centerline1[1:-1]
# smooth centerline
centerline2 = smooth_centerline(centerline1, 20)

centerline_nodes = pp_to_graph(centerline2)

f = vv.figure(1)
vv.clf()
f.position = 709.00, 30.00, 1203.00, 1008.00
a1 = vv.subplot(121)
vv.plot(pp, ms='.', ls='', alpha=0.2, mw=7)  # stent seed points
vv.plot(PointSet(list(start1)), ms='.', ls='', mc='g', mw=18)  # start1
vv.plot(PointSet(list(end1)), ms='.', ls='', mc='m', mw=16)
vv.plot(centerline1, ms='.', ls='', mw=8, mc='c')
vv.plot(centerline2, ms='.', ls='', mw=8, mc='y')  # smoothed
a1.axis.visible = showAxis
a1.daspect = 1, 1, -1

a2 = vv.subplot(122)
show_ctvolume(s.vol,
              None,
              showVol=showVol,
              clim=clim0,
              isoTh=isoTh,
              removeStent=False)
label = pick3d(vv.gca(), s.vol)
vv.plot(PointSet(list(start1)), ms='.', ls='', mc='g', mw=18)  # start1
Beispiel #20
0
showAxis = True  # True or False
showVol = 'ISO'  # MIP or ISO or 2D or None
ringpart = True  # True; False
clim0 = (0, 2500)
# clim0 = -550,500
isoTh = 250
meshradius = 0.7

# create class object for excel analysis
foo = ExcelAnalysis()  # excel locations initialized in class

## Renal origin coordinates: input by user/read excel
# coordinates, left and right most caudal renal
# ctcode1
xrenal1, yrenal1, zrenal1 = 132.7, 89.2, 85.5
renal1 = PointSet(list((xrenal1, yrenal1, zrenal1)))
# ctcode2
if ctcode2:
    xrenal2, yrenal2, zrenal2 = 171, 165.1, 39.5
    renal2 = PointSet(list((xrenal2, yrenal2, zrenal2)))

# renal_left, renal_right = foo.readRenalsExcel(sheet_renals_obs, ptcode, ctcode1)
# renal1 = renal_left

## Load (dynamic) stent models, vessel, ct
# Load static CT image to add as reference
s = loadvol(basedir, ptcode, ctcode1, cropname, 'avgreg')
vol1 = s.vol
if ctcode2:
    s = loadvol(basedir, ptcode, ctcode2, cropname, 'avgreg')
    vol2 = s.vol
Beispiel #21
0
def find_centerline(pp,
                    start,
                    ends,
                    step,
                    *,
                    substep=None,
                    ndist=20,
                    regfactor=0.2,
                    regsteps=10,
                    verbose=False):
    """ Find path from a start point to an end-point or set of end-points.
    Starting from start, will try to find a route to the closest
    of the given end-points, while maximizing the distance to the points
    in pp. Works on 2D and 3D points.
    
    Arguments:
        pp (Pointset): points to stay away from. Typically points on the
            structure that we are trying to find a centerline for.
        start (tuple, PointSet): Point or tuple indicating the start position.
        ends (tuple, list, Pointset): Point or points indicating end positions.
        step (float): the stepsize of points along the centerline.
        substep (float, optional): the stepsize for the candiate
            positions. Default is 0.2 of step.
        ndist (int): the number of closest points to take into account in
            the distance measurements. Default 20.
        regfactor (float): regularization factor between 0 and 1. Default 0.2.
        regsteps (float): Distance in number of steps from start/end at which
            there is no additional "endpoint" regularization. Default 10.
        verbose (bool): if True, print info.
    Returns a Pointset with points that are each step distance from
    each-other (except for the last step). The first point matches the
    start position and the last point matches one of the end positions.
    """

    substep = substep or step * 0.2

    # Check pp and ndim
    if not isinstance(pp, PointSet):
        raise ValueError('pp must be a pointset')
    dims = pp.shape[1]
    if dims not in (2, 3):
        raise ValueError('find_centerline() only works on 2D or 3D data')

    # Prepare start
    start = np.array(start)
    start.shape = -1, dims
    start = PointSet(start)
    assert start.shape[0] == 1
    assert start.shape[1] == dims

    # Prepare ends
    ends = np.array(ends)
    ends.shape = -1, dims
    ends = PointSet(ends)
    assert ends.shape[1] == dims

    # Init
    centerline = PointSet(dims)
    pos = start

    # Prepare for first iter
    centerline.append(pos)
    end = _closest_point(ends, pos)
    vec = (end - pos).normalize() * step

    n_estimate = (end - pos).norm() / step
    i = 0
    while pos.distance(end) > step:
        i += 1
        measure = _distance_measure(pp, pos, ndist)

        #if i > n_estimate * 2 or measure > 250: # * 4:
        if i > n_estimate * 2 or measure > 60:  # return sooner
            print('i={} and n_estimate={}; measure={}'.format(
                i, n_estimate, measure))
            print('We seem to be lost. Centerline is returned')
            return centerline
            # raise RuntimeError('We seem to be lost')

        if verbose:
            print('iter %i, distance %1.1f of %1.1f' %
                  (i, pos.distance(end), start.distance(end)))

        # Estimated position and refine using distance measure
        # Step towards end-pos, but refine by looking orthogonal to real direction
        pos1 = pos + vec
        pos2 = _find_better_pos(pp, pos1, vec, substep, ndist)

        # Calculate damp factor to direct regularization towards start-end direction
        reg_damper = 1.0 - float(min(pos.distance(end),
                                     pos1.distance(start))) / (regsteps * step)
        reg_damper = min(1.0, max(reg_damper, 0))

        # Combine original estimate with distance-based position -> regularization
        reg = min(1, regfactor + reg_damper)
        refpos = reg * pos1 + (1 - reg) * pos2

        # Rescale so we take equally sized steps
        vec_real = (refpos - pos).normalize()
        pos = pos + vec_real * step

        # Store and prepare for next step
        centerline.append(pos)
        end = _closest_point(ends, pos)
        vec = ((1 - reg_damper) * vec_real +
               (end - pos).normalize()).normalize() * step

    # Wrap up
    centerline.append(end)
    return centerline
ppmodelvessel = points_from_nodes_in_graph(s_modelvessel.model)

s_model = loadmodel(basedir, ptcode, ctcode, 'prox', modelname='modelavgreg')
ppmodel = points_from_nodes_in_graph(s_model.model)

# Visualize
AC.a.MakeCurrent()
vv.cla()  # clear vol otherwise plots not visible somehow

# seedpoints segmentations
vv.plot(ppmodelvessel, ms='.', ls='', alpha=0.6, mw=2)
vv.plot(ppmodel, ms='.', ls='', alpha=0.6, mw=2)

# stents
for j in range(len(stentsStartPoints)):
    vv.plot(PointSet(list(stentsStartPoints[j])), ms='.', ls='', mc='g',
            mw=20)  # startpoint green
    vv.plot(PointSet(list(stentsEndPoints[j])), ms='.', ls='', mc='r',
            mw=20)  # endpoint red
for j in range(len(allcenterlines)):
    vv.plot(allcenterlines[j], ms='.', ls='', mw=10, mc='y')
# vessels
for j in range(len(vesselStartPoints)):
    vv.plot(PointSet(list(vesselStartPoints[j])), ms='.', ls='', mc='g',
            mw=20)  # startpoint green
    vv.plot(PointSet(list(vesselEndPoints[j])), ms='.', ls='', mc='r',
            mw=20)  # endpoint red
for j in range(len(allcenterlinesv)):
    vv.plot(allcenterlinesv[j], ms='.', ls='', mw=10, mc='y')

vv.title('Centerlines and seed points')
Beispiel #23
0
import os

from stentseg.utils import PointSet
from stentseg.utils.centerline import find_centerline, points_from_mesh, smooth_centerline, pp_to_graph
from stentseg.utils.datahandling import select_dir, loadvol, loadmodel
from stentseg.utils.visualization import show_ctvolume
from stentseg.stentdirect import stentgraph

TEST = 14

if TEST == 1:
    import imageio
    im = imageio.imread('~/Desktop/test_centerline.png')[:, :, 0]

    y, x = np.where(im < 200)
    pp = PointSet(np.column_stack([x, y]))

    start = (260, 60)
    ends = [(230, 510), (260, 510), (360, 510)]
    centerline = find_centerline(pp,
                                 start,
                                 ends,
                                 8,
                                 ndist=20,
                                 regfactor=0.2,
                                 regsteps=10)

    vv.figure(1)
    vv.clf()
    a1 = vv.subplot(111)
    vv.plot(pp, ms='.', ls='')
Beispiel #24
0
def take_measurements(measure_volume_change):
    """ This gets called when the slider is releases. We take measurements and
    update the corresponding texts and visualizations.
    """

    # Get points that form the contour of the vessel in 2D
    pp = get_plane_points_from_centerline_index(slider_ves.value)
    pp2, pp3 = get_vessel_points_from_plane_points(pp)
    plane = pp2.plane

    # Collect measurements in a dict. That way we can process it in one step at the end
    measurements = {}

    # Store slider positions, so we can reproduce this measurement later
    measurements["centerline indices"] = slider_ref.value, slider_ves.value

    # Early exit?
    if len(pp2) == 0:
        line_2d.SetPoints(pp2)
        line_ellipse1.SetPoints(pp2)
        line_ellipse2.SetPoints(pp2)
        vesselVisMesh2.SetFaces(np.zeros((3, 3), np.int32))
        vesselVisMesh2.SetNormals(None)
        process_measurements(measurements)
        return

    # Measure length of selected part of the centerline and the strain in that section
    measurements["centerline distance"] = get_distance_along_centerline()
    measurements["centerline strain"] = measure_centerline_strain()

    # Measure centerline curvature
    curvature_mean, curvature_max, curvature_max_pos, curvature_max_change = measure_curvature(
        centerline, deforms)
    measurements["curvature mean"] = DeformInfo(curvature_mean)
    measurements["curvature max"] = DeformInfo(curvature_max)
    measurements["curvature max pos"] = DeformInfo(curvature_max_pos)
    measurements["curvature max change"] = curvature_max_change

    # Get ellipse and its center point
    ellipse = fitting.fit_ellipse(pp2)
    p0 = PointSet([ellipse[0], ellipse[1]])

    # Sample ellipse to calculate its area
    pp_ellipse = fitting.sample_ellipse(ellipse,
                                        256)  # results in N + 1 points
    area = 0
    for i in range(len(pp_ellipse) - 1):
        area += triangle_area(p0, pp_ellipse[i], pp_ellipse[i + 1])
    # measurements["reference area"] = float(area)
    # Do a quick check to be sure that this triangle-approximation is close enough
    assert abs(area - fitting.area(ellipse)
               ) < 2, "area mismatch"  # mm2  typically ~ 0.1 mm2

    # Measure ellipse area (and how it changes)
    measurements["ellipse area"] = DeformInfo(unit="mm2")
    for pp_ellipse_def in deform_points_2d(pp_ellipse, plane):
        area = 0
        for i in range(len(pp_ellipse_def) - 1):
            area += triangle_area(p0, pp_ellipse_def[i], pp_ellipse_def[i + 1])
        measurements["ellipse area"].append(area)

    # # Measure expansion of ellipse in 256 locations?
    # # Measure distances from center to ellipse edge. We first get the distances
    # # in each face, for each point. Then we aggregate these distances to
    # # expansion measures. So in the end we have 256 expansion measures.
    # distances_per_point = [[] for i in range(len(pp_ellipse))]
    # for pp_ellipse_def in deform_points_2d(pp_ellipse, plane):
    #     # todo: Should distance be measured to p0 or to p0 in that phase?
    #     for i, d in enumerate(pp_ellipse_def.distance(p0)):
    #         distances_per_point[i].append(float(d))
    # distances_per_point = distances_per_point[:-1]  # Because pp_ellipse[-1] == pp_ellipse[0]
    # #
    # measurements["expansions"] = DeformInfo()  # 256 values, not 10
    # for i in range(len(distances_per_point)):
    #     distances = distances_per_point[i]
    #     measurements["expansions"].append((max(distances) - min(distances)) / min(distances))

    # Measure radii of ellipse major and minor axis (and how it changes)
    pp_ellipse4 = fitting.sample_ellipse(ellipse,
                                         4)  # major, minor, major, minor
    measurements["ellipse expansion major1"] = DeformInfo(unit="mm")
    measurements["ellipse expansion minor1"] = DeformInfo(unit="mm")
    measurements["ellipse expansion major2"] = DeformInfo(unit="mm")
    measurements["ellipse expansion minor2"] = DeformInfo(unit="mm")
    for pp_ellipse4_def in deform_points_2d(pp_ellipse4, plane):
        measurements["ellipse expansion major1"].append(
            float(pp_ellipse4_def[0].distance(p0)))
        measurements["ellipse expansion minor1"].append(
            float(pp_ellipse4_def[1].distance(p0)))
        measurements["ellipse expansion major2"].append(
            float(pp_ellipse4_def[2].distance(p0)))
        measurements["ellipse expansion minor2"].append(
            float(pp_ellipse4_def[3].distance(p0)))

    # Measure how the volume changes - THIS BIT IS COMPUTATIONALLY EXPENSIVE
    submesh = meshlib.Mesh(np.zeros((3, 3)))
    if measure_volume_change:
        # Update the submesh
        plane1 = fitting.fit_plane(
            get_plane_points_from_centerline_index(slider_ref.value))
        plane2 = fitting.fit_plane(
            get_plane_points_from_centerline_index(slider_ves.value))
        plane2 = [-x for x in plane2]  # flip the plane upside doen
        submesh = vesselMesh.cut_plane(plane1).cut_plane(plane2)
        # Measure its motion
        measurements["volume"] = DeformInfo(unit="mm3")
        submesh._ori_vertices = submesh._vertices.copy()
        for phase in range(len(deforms)):
            deform = deforms[phase]
            submesh._vertices = submesh._ori_vertices.copy()
            dx = deform.get_field_in_points(submesh._vertices, 0)
            dy = deform.get_field_in_points(submesh._vertices, 1)
            dz = deform.get_field_in_points(submesh._vertices, 2)
            submesh._vertices[:, 0] += dx
            submesh._vertices[:, 1] += dy
            submesh._vertices[:, 2] += dz
            measurements["volume"].append(submesh.volume())

    # Show measurements
    process_measurements(measurements)

    # Update line objects
    line_2d.SetPoints(pp2)
    line_ellipse1.SetPoints(fitting.sample_ellipse(ellipse))
    major_minor = PointSet(2)
    for p in [
            p0, pp_ellipse4[0], p0, pp_ellipse4[2], p0, pp_ellipse4[1], p0,
            pp_ellipse4[3]
    ]:
        major_minor.append(p)
    line_ellipse2.SetPoints(major_minor)
    axes2.SetLimits(margin=0.12)

    # Update submesh object
    vertices, faces = submesh.get_vertices_and_faces()
    vesselVisMesh2.SetVertices(vertices)
    vesselVisMesh2.SetFaces(
        np.zeros((3, 3), np.int32) if len(faces) == 0 else faces)
    vesselVisMesh2.SetNormals(None)
Beispiel #25
0
    deforms = [pirt.DeformationFieldBackward(*fields) for fields in deforms]

# Load vessel mesh (mimics)
# We make sure that it is a mesh without faces, which makes our sampling easier
try:
    ppvessel
except NameError:
    # Load mesh with visvis, then put in our meshlib.Mesh() and let it ensure that
    # the mesh is closed, check the winding, etc. so that we can cut it with planes,
    # and reliably calculate volume.
    filename = '{}_{}_neck.stl'.format(ptcode, ctcode1)
    vesselMesh = loadmesh(basedirMesh, ptcode[-3:], filename)  #inverts Z
    vv.processing.unwindFaces(vesselMesh)
    vesselMesh = meshlib.Mesh(vesselMesh._vertices)
    vesselMesh.ensure_closed()
    ppvessel = PointSet(vesselMesh.get_flat_vertices())  # Must be flat!

# Load ring model
try:
    modelmesh1
except NameError:
    s1 = loadmodel(basedir, ptcode, ctcode1, cropname, modelname)
    if drawRingMesh:
        if not ringMeshDisplacement:
            modelmesh1 = create_mesh(s1.model, 0.7)  # Param is thickness
        else:
            modelmesh1 = create_mesh_with_abs_displacement(s1.model,
                                                           radius=0.7,
                                                           dim=dimensions)

# Load vessel centerline (excel terarecon) (is very fast)
Beispiel #26
0
    def __init__(self,
                 ptcode,
                 ctcode,
                 StartPoints,
                 EndPoints,
                 basedir,
                 modelname='modelavgreg'):
        """ with start and endpoints provided, calculate centerline and save as
        ssdf in basedir as model and dynamic model
        """
        #todo: name of dynamic model is now deforms, unclear, should be dynamic
        #import numpy as np
        import visvis as vv
        import numpy as np
        import os
        import copy

        from stentseg.utils import PointSet, _utils_GUI
        from stentseg.utils.centerline import (find_centerline,
                                               points_from_nodes_in_graph,
                                               points_from_mesh,
                                               smooth_centerline)
        from stentseg.utils.datahandling import loadmodel, loadvol
        from stentseg.utils.visualization import show_ctvolume
        from stentseg.utils.picker import pick3d

        stentnr = len(StartPoints)

        cropname = 'prox'
        what = modelname
        what_vol = 'avgreg'
        vismids = True
        m = loadmodel(basedir, ptcode, ctcode, cropname, what)
        s = loadvol(basedir, ptcode, ctcode, cropname, what_vol)
        s.vol.sampling = [s.sampling[1], s.sampling[1], s.sampling[2]]
        s.sampling = s.vol.sampling

        start1 = StartPoints.copy()
        ends = EndPoints.copy()

        from stentseg.stentdirect import stentgraph
        ppp = points_from_nodes_in_graph(m.model)

        allcenterlines = []  # for pp
        allcenterlines_nosmooth = []  # for pp
        centerlines = []  # for stentgraph
        nodes_total = stentgraph.StentGraph()
        for j in range(stentnr):
            if j == 0 or not start1[j] == ends[j - 1]:
                # if first stent or when stent did not continue with this start point
                nodes = stentgraph.StentGraph()
                centerline = PointSet(3)  # empty

            # Find main centerline
            # if j > 3: # for stent with midpoints
            #     centerline1 = find_centerline(ppp, start1[j], ends[j], step= 1,
            #     ndist=10, regfactor=0.5, regsteps=10, verbose=False)

            #else:
            centerline1 = find_centerline(ppp,
                                          start1[j],
                                          ends[j],
                                          step=1,
                                          ndist=10,
                                          regfactor=0.5,
                                          regsteps=1,
                                          verbose=False)
            # centerline1 is a PointSet

            print('Centerline calculation completed')

            # ========= Maaike =======
            smoothfactor = 15  # Mirthe used 2 or 4

            # check if cll continued here from last end point
            if not j == 0 and start1[j] == ends[j - 1]:
                # yes we continued
                ppart = centerline1[:
                                    -1]  # cut last but do not cut first point as this is midpoint
            else:
                # do not use first points, as they are influenced by user selected points
                ppart = centerline1[1:-1]

            for p in ppart:
                centerline.append(p)

            # if last stent or stent does not continue with next start-endpoint
            if j == stentnr - 1 or not ends[j] == start1[j + 1]:
                # store non-smoothed for vis
                allcenterlines_nosmooth.append(centerline)
                pp = smooth_centerline(centerline, n=smoothfactor)
                # add pp to list
                allcenterlines.append(pp)  # list with PointSet per centerline
                self.allcenterlines = allcenterlines

                # add pp as nodes
                for i, p in enumerate(pp):
                    p_as_tuple = tuple(p.flat)
                    nodes.add_node(p_as_tuple)
                    nodes_total.add_node(p_as_tuple)
                # add pp as one edge so that pathpoints are in fixed order
                pstart = tuple(pp[0].flat)
                pend = tuple(pp[-1].flat)
                nodes.add_edge(pstart, pend, path=pp)
                nodes_total.add_edge(pstart, pend, path=pp)
                # add final centerline nodes model to list
                centerlines.append(nodes)

            # ========= Maaike =======

        ## Store segmentation to disk

        # Build struct
        s2 = vv.ssdf.new()
        s2.sampling = s.sampling
        s2.origin = s.origin
        s2.stenttype = m.stenttype
        s2.croprange = m.croprange
        for key in dir(m):
            if key.startswith('meta'):
                suffix = key[4:]
                s2['meta' + suffix] = m['meta' + suffix]
        s2.what = what
        s2.params = s.params  #reg
        s2.paramsseeds = m.params
        s2.stentType = 'nellix'
        s2.StartPoints = StartPoints
        s2.EndPoints = EndPoints
        # keep centerlines as pp also [Maaike]
        s2.ppallCenterlines = allcenterlines
        for k in range(len(allcenterlines)):
            suffix = str(k)
            pp = allcenterlines[k]
            s2['ppCenterline' + suffix] = pp

        s3 = copy.deepcopy(s2)
        s3['model'] = nodes_total.pack()

        # Store model for each centerline
        for j in range(len(centerlines)):
            suffix = str(j)
            model = centerlines[j]
            s2['model' + suffix] = model.pack()

        # Save model with seperate centerlines.
        filename = '%s_%s_%s_%s.ssdf' % (ptcode, ctcode, cropname,
                                         'centerline_' + what)
        vv.ssdf.save(os.path.join(basedir, ptcode, filename), s2)
        print('saved to disk as {}.'.format(filename))

        # Save model with combined centerlines
        filename = '%s_%s_%s_%s.ssdf' % (ptcode, ctcode, cropname,
                                         'centerline_total_' + what)
        vv.ssdf.save(os.path.join(basedir, ptcode, filename), s3)
        print('saved to disk as {}.'.format(filename))

        # remove intermediate centerline points
        # start1 = map(tuple, start1)
        # ends = map(tuple, ends)
        startpoints_clean = copy.deepcopy(start1)
        endpoints_clean = copy.deepcopy(ends)
        duplicates = list(set(start1) & set(ends))
        for i in range(len(duplicates)):
            startpoints_clean.remove(duplicates[i])
            endpoints_clean.remove(duplicates[i])

        #Visualize
        f = vv.figure(10)
        vv.clf()
        a1 = vv.subplot(121)
        a1.daspect = 1, 1, -1

        vv.plot(ppp, ms='.', ls='', alpha=0.6, mw=2)
        for j in range(len(startpoints_clean)):
            vv.plot(PointSet(list(startpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='g',
                    mw=20)  # startpoint green
            vv.plot(PointSet(list(endpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='r',
                    mw=20)  # endpoint red
        for j in range(len(allcenterlines)):
            vv.plot(allcenterlines[j], ms='.', ls='', mw=10, mc='y')
        vv.title('Centerlines and seed points')
        vv.xlabel('x (mm)')
        vv.ylabel('y (mm)')
        vv.zlabel('z (mm)')
        # for j in range(len(allcenterlines_nosmooth)):
        #     vv.plot(allcenterlines_nosmooth[j], ms='o', ls='', mw=10, mc='c', alpha=0.6)

        a2 = vv.subplot(122)
        a2.daspect = 1, 1, -1

        vv.plot(ppp, ms='.', ls='', alpha=0.6, mw=2)
        # vv.volshow(s.vol, clim=clim, renderStyle = 'mip')
        t = show_ctvolume(s.vol,
                          axis=a2,
                          showVol='ISO',
                          clim=(0, 2500),
                          isoTh=250,
                          removeStent=False,
                          climEditor=True)
        label = pick3d(vv.gca(), s.vol)
        for j in range(len(startpoints_clean)):
            vv.plot(PointSet(list(startpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='g',
                    mw=20,
                    alpha=0.6)  # startpoint green
            vv.plot(PointSet(list(endpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='r',
                    mw=20,
                    alpha=0.6)  # endpoint red
        for j in range(len(allcenterlines)):
            vv.plot(allcenterlines[j], ms='o', ls='', mw=10, mc='y', alpha=0.6)

        # show midpoints (e.g. duplicates)
        if vismids:
            for p in duplicates:
                vv.plot(p[0], p[1], p[2], mc='m', ms='o', mw=10, alpha=0.6)

        a2.axis.visible = False

        vv.title('Centerlines and seed points')

        a1.camera = a2.camera

        f.eventKeyDown.Bind(
            lambda event: _utils_GUI.RotateView(event, [a1, a2]))
        f.eventKeyDown.Bind(
            lambda event: _utils_GUI.ViewPresets(event, [a1, a2]))

        # Pick node for midpoint to redo get_centerline
        self.pickedCLLpoint = _utils_GUI.Event_pick_graph_point(
            nodes_total, s.vol, label, nodesOnly=True)  # x,y,z
        # use key p to select point

        #===============================================================================
        vv.figure(11)
        vv.gca().daspect = 1, 1, -1
        t = show_ctvolume(s.vol,
                          showVol='ISO',
                          clim=(0, 2500),
                          isoTh=250,
                          removeStent=False,
                          climEditor=True)
        label2 = pick3d(vv.gca(), s.vol)
        for j in range(len(startpoints_clean)):
            vv.plot(PointSet(list(startpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='g',
                    mw=20,
                    alpha=0.6)  # startpoint green
            vv.plot(PointSet(list(endpoints_clean[j])),
                    ms='.',
                    ls='',
                    mc='r',
                    mw=20,
                    alpha=0.6)  # endpoint red
        vv.xlabel('x (mm)')
        vv.ylabel('y (mm)')
        vv.zlabel('z (mm)')
        #===============================================================================

        ## Make model dynamic (and store/overwrite to disk)
        import pirt
        from stentseg.motion.dynamic import incorporate_motion_nodes, incorporate_motion_edges

        # Load deforms
        filename = '%s_%s_%s_%s.ssdf' % (ptcode, ctcode, cropname, 'deforms')
        s1 = vv.ssdf.load(os.path.join(basedir, ptcode, filename))
        deformkeys = []
        for key in dir(s1):
            if key.startswith('deform'):
                deformkeys.append(key)
        deforms = [s1[key] for key in deformkeys]
        deforms = [
            pirt.DeformationFieldBackward(*fields) for fields in deforms
        ]
        for i in range(len(deforms)):
            deforms[i]._field_sampling = tuple(s1.sampling)
        paramsreg = s1.params

        # Load model
        s2 = loadmodel(basedir, ptcode, ctcode, cropname, 'centerline_' + what)
        s3 = loadmodel(basedir, ptcode, ctcode, cropname,
                       'centerline_total_' + what)

        # Combine ...
        for key in dir(s2):
            if key.startswith('model'):
                incorporate_motion_nodes(s2[key], deforms, s.origin)
                incorporate_motion_edges(s2[key], deforms, s.origin)
                model = s2[key]
                s2[key] = model.pack()
        # Combine ...
        for key in dir(s3):
            if key.startswith('model'):
                incorporate_motion_nodes(s3[key], deforms, s.origin)
                incorporate_motion_edges(s3[key], deforms, s.origin)
                model = s3[key]
                s3[key] = model.pack()

        # Save
        s2.paramsreg = paramsreg
        filename = '%s_%s_%s_%s.ssdf' % (ptcode, ctcode, cropname,
                                         'centerline_' + what + '_deforms')
        vv.ssdf.save(os.path.join(basedir, ptcode, filename), s2)
        print('saved to disk as {}.'.format(filename))

        # Save
        s3.paramsreg = paramsreg
        filename = '%s_%s_%s_%s.ssdf' % (
            ptcode, ctcode, cropname, 'centerline_total_' + what + '_deforms')
        vv.ssdf.save(os.path.join(basedir, ptcode, filename), s3)
        print('saved to disk as {}.'.format(filename))