Module pymskt.mesh.anatomical.femur_long_axis

Expand source code
import numpy as np
from vtk.util.numpy_support import vtk_to_numpy
import pyvista as pv
from pymskt.statistics.pca import pca_svd

class FitLongAxisFemur:
    def __init__(
        self,
        femur,
        labels_name='labels',
        cart_label=1,
        bone_label=0,
        percent_epiph_higher=0.3,
        n_pts=100,
        buffer=1,
        use_center_pts_only=True
        
    ):
        self.femur = femur
        self.labels_name = labels_name
        self.cart_label = cart_label
        self.bone_label = bone_label
        self.percent_epiph_higher = percent_epiph_higher
        self.n_pts = n_pts
        self.buffer = buffer
        self.use_center_pts_only = use_center_pts_only
        
        self._bone_pts = None
        self._cart_pts = None
        self.min_cart = None
        self.min_bone = None
        self.max_cart = None
        self.max_bone = None
        
        self.long_axis = None
        self.proximal = None
        
        self.origin = None
        
        self._diaph_points = None
        self._epiph_points = None
        
        self._vector = None
        
    
    def get_bone_cart_points(self):
        labels = vtk_to_numpy(self.femur.mesh.GetPointData().GetArray(self.labels_name))
        if (type(self.cart_label) == list) or (type(self.cart_label) == tuple):
            cart_indices = None
            for cart_label in self.cart_label:
                if cart_indices is None:
                    cart_indices = labels == cart_label
                cart_indices += labels == cart_label
        else:
            cart_indices = labels == self.cart_label
        cart_ids = np.where(cart_indices)
        cart_pts = np.squeeze(self.femur.point_coords[cart_ids,:])
        bone_ids = np.where(labels == self.bone_label)
        bone_pts = np.squeeze(self.femur.point_coords[bone_ids,:])

        self._bone_pts = bone_pts
        self._cart_pts = cart_pts

    def get_min_max_bone_cart(self):
        if (self._bone_pts is None) or (self._cart_pts is None):
            self.get_bone_cart_points()

        self.max_cart = np.max(self._cart_pts, axis=0)
        self.max_bone = np.max(self._bone_pts, axis=0)

        self.min_cart = np.min(self._cart_pts, axis=0)
        self.min_bone = np.min(self._bone_pts, axis=0)

    def get_long_axis_and_proximal_direction(self):
        if (self._bone_pts is None) or (self._cart_pts is None):
            self.get_bone_cart_points()
        if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
                self.get_min_max_bone_cart()

        cart_range = self.max_cart - self.min_cart
        bone_range = self.max_bone - self.min_bone
        long_axis = np.argmax(np.abs(cart_range - bone_range))

        if np.abs(self.max_cart[2] - self.max_bone[2]) > np.abs(self.min_cart[2] - self.min_bone[2]):
            proximal = int(1)
        else:
            proximal = int(-1)
        
        self.proximal = proximal
        self.long_axis = long_axis

    def guess_origin(self):
        if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
                self.get_min_max_bone_cart()

        epiphysis_height = self.max_cart[self.long_axis] - self.min_cart[self.long_axis]

        origin = np.mean(self.femur.point_coords, axis=0)

        if self.proximal == 1:
            origin[self.long_axis] = self.max_cart[self.long_axis] + epiphysis_height * self.percent_epiph_higher # add 5mm buffer?    
        if self.proximal == -1:
            origin[self.long_axis] = self.min_cart[self.long_axis] - epiphysis_height * self.percent_epiph_higher # add 5mm buffer? 
        
        self.origin = origin

    def get_diaph_epiph_points(self):
        if (self.long_axis is None) or (self.proximal is None):
            self.get_long_axis_and_proximal_direction()
        if self.origin is None:
            self.guess_origin()
            
        # NORMAL MUST HAVE MAGNITUDE OF 1
        normal = np.zeros(3)
        normal[self.long_axis] = 1

        # create a random point on the plane we want to slice from. 
        point_on_plane = np.random.random_sample(size=3) * 20 - 10 # make random sample in range [-10, 10]
        point_on_plane[self.long_axis] = self.origin[self.long_axis]

        side = normal @ (self.femur.point_coords - point_on_plane).T

        diaph_points = self.femur.point_coords[side > 0, :]
        epiph_points = self.femur.point_coords[side < 0, :]
        
        pv_femur = pv.PolyData(self.femur.mesh)
        pv_diaph, _ = pv_femur.remove_points(side<0)
        
        self.pv_diaph = pv_diaph
        self._diaph_points = diaph_points
        self._epiph_points = epiph_points
        

    def get_diaph_vector(self):
        if (self._diaph_points is None) or (self._epiph_points is None) or (self.pv_diaph):
            self.get_diaph_epiph_points()            
        
        min_long = np.min(self._diaph_points[:, self.long_axis])
        max_long = np.max(self._diaph_points[:, self.long_axis])
        if self.proximal == 1:
            min_ = min_long + self.buffer
            max_ = max_long - self.buffer
        elif self.proximal == -1:
            max_ = max_long - self.buffer
            min_ = min_long + self.buffer

        normal = [0, 0, 0]
        normal[self.long_axis] = 1

        centers = []

        for idx, depth in enumerate(np.linspace(min_, max_, self.n_pts)):
            # get slice
            origin_ = [0, 0, 0]
            origin_[self.long_axis] = depth
            slice_ = self.pv_diaph.slice(normal=normal, origin=origin_)
            # get points from slice
            slice_pts = slice_.points

            if self.use_center_pts_only is True:
                ptp = np.ptp(slice_pts, axis=0)
                min_ = np.min(slice_pts, axis=0)
                middle = min_ + ptp/2
                lower = middle - 0.05 * ptp
                upper = middle + 0.05 * ptp

                pts = None
                for axis in range(3):
                    if ptp[axis] == 0:
                        pass
                    else:
                        indices = (slice_pts[:,axis] < upper[axis]) * (slice_pts[:,axis] > lower[axis])
                        pts_ = slice_pts[indices, :]
                        if pts is None:
                            pts = pts_
                        else:
                            pts = np.append(pts, pts_, axis=0)
            #             pts.append()
            elif self.use_center_pts_only is False:
                pts = slice_.points

            centroid = np.mean(pts, axis=0)
            # append Y-position of that point to the `Ys` list
            centers.append(centroid)
        centers = np.asarray(centers)

        inertial_matrix, _ = pca_svd(centers.T)

        vector = inertial_matrix[:,0]
        
        self._vector = vector
    
    def fit(self):
        self.get_diaph_vector()
    
    @property
    def vector(self):
        return self._vector
    
    @property
    def diaph_points(self):
        return self._diaph_points
    
    @property
    def epiph_points(self):
        return self._epiph_points
    
    @property
    def cart_pts(self):
        return self._cart_pts
    
    @property
    def bone_pts(self):
        return self._bone_pts

Classes

class FitLongAxisFemur (femur, labels_name='labels', cart_label=1, bone_label=0, percent_epiph_higher=0.3, n_pts=100, buffer=1, use_center_pts_only=True)
Expand source code
class FitLongAxisFemur:
    def __init__(
        self,
        femur,
        labels_name='labels',
        cart_label=1,
        bone_label=0,
        percent_epiph_higher=0.3,
        n_pts=100,
        buffer=1,
        use_center_pts_only=True
        
    ):
        self.femur = femur
        self.labels_name = labels_name
        self.cart_label = cart_label
        self.bone_label = bone_label
        self.percent_epiph_higher = percent_epiph_higher
        self.n_pts = n_pts
        self.buffer = buffer
        self.use_center_pts_only = use_center_pts_only
        
        self._bone_pts = None
        self._cart_pts = None
        self.min_cart = None
        self.min_bone = None
        self.max_cart = None
        self.max_bone = None
        
        self.long_axis = None
        self.proximal = None
        
        self.origin = None
        
        self._diaph_points = None
        self._epiph_points = None
        
        self._vector = None
        
    
    def get_bone_cart_points(self):
        labels = vtk_to_numpy(self.femur.mesh.GetPointData().GetArray(self.labels_name))
        if (type(self.cart_label) == list) or (type(self.cart_label) == tuple):
            cart_indices = None
            for cart_label in self.cart_label:
                if cart_indices is None:
                    cart_indices = labels == cart_label
                cart_indices += labels == cart_label
        else:
            cart_indices = labels == self.cart_label
        cart_ids = np.where(cart_indices)
        cart_pts = np.squeeze(self.femur.point_coords[cart_ids,:])
        bone_ids = np.where(labels == self.bone_label)
        bone_pts = np.squeeze(self.femur.point_coords[bone_ids,:])

        self._bone_pts = bone_pts
        self._cart_pts = cart_pts

    def get_min_max_bone_cart(self):
        if (self._bone_pts is None) or (self._cart_pts is None):
            self.get_bone_cart_points()

        self.max_cart = np.max(self._cart_pts, axis=0)
        self.max_bone = np.max(self._bone_pts, axis=0)

        self.min_cart = np.min(self._cart_pts, axis=0)
        self.min_bone = np.min(self._bone_pts, axis=0)

    def get_long_axis_and_proximal_direction(self):
        if (self._bone_pts is None) or (self._cart_pts is None):
            self.get_bone_cart_points()
        if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
                self.get_min_max_bone_cart()

        cart_range = self.max_cart - self.min_cart
        bone_range = self.max_bone - self.min_bone
        long_axis = np.argmax(np.abs(cart_range - bone_range))

        if np.abs(self.max_cart[2] - self.max_bone[2]) > np.abs(self.min_cart[2] - self.min_bone[2]):
            proximal = int(1)
        else:
            proximal = int(-1)
        
        self.proximal = proximal
        self.long_axis = long_axis

    def guess_origin(self):
        if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
                self.get_min_max_bone_cart()

        epiphysis_height = self.max_cart[self.long_axis] - self.min_cart[self.long_axis]

        origin = np.mean(self.femur.point_coords, axis=0)

        if self.proximal == 1:
            origin[self.long_axis] = self.max_cart[self.long_axis] + epiphysis_height * self.percent_epiph_higher # add 5mm buffer?    
        if self.proximal == -1:
            origin[self.long_axis] = self.min_cart[self.long_axis] - epiphysis_height * self.percent_epiph_higher # add 5mm buffer? 
        
        self.origin = origin

    def get_diaph_epiph_points(self):
        if (self.long_axis is None) or (self.proximal is None):
            self.get_long_axis_and_proximal_direction()
        if self.origin is None:
            self.guess_origin()
            
        # NORMAL MUST HAVE MAGNITUDE OF 1
        normal = np.zeros(3)
        normal[self.long_axis] = 1

        # create a random point on the plane we want to slice from. 
        point_on_plane = np.random.random_sample(size=3) * 20 - 10 # make random sample in range [-10, 10]
        point_on_plane[self.long_axis] = self.origin[self.long_axis]

        side = normal @ (self.femur.point_coords - point_on_plane).T

        diaph_points = self.femur.point_coords[side > 0, :]
        epiph_points = self.femur.point_coords[side < 0, :]
        
        pv_femur = pv.PolyData(self.femur.mesh)
        pv_diaph, _ = pv_femur.remove_points(side<0)
        
        self.pv_diaph = pv_diaph
        self._diaph_points = diaph_points
        self._epiph_points = epiph_points
        

    def get_diaph_vector(self):
        if (self._diaph_points is None) or (self._epiph_points is None) or (self.pv_diaph):
            self.get_diaph_epiph_points()            
        
        min_long = np.min(self._diaph_points[:, self.long_axis])
        max_long = np.max(self._diaph_points[:, self.long_axis])
        if self.proximal == 1:
            min_ = min_long + self.buffer
            max_ = max_long - self.buffer
        elif self.proximal == -1:
            max_ = max_long - self.buffer
            min_ = min_long + self.buffer

        normal = [0, 0, 0]
        normal[self.long_axis] = 1

        centers = []

        for idx, depth in enumerate(np.linspace(min_, max_, self.n_pts)):
            # get slice
            origin_ = [0, 0, 0]
            origin_[self.long_axis] = depth
            slice_ = self.pv_diaph.slice(normal=normal, origin=origin_)
            # get points from slice
            slice_pts = slice_.points

            if self.use_center_pts_only is True:
                ptp = np.ptp(slice_pts, axis=0)
                min_ = np.min(slice_pts, axis=0)
                middle = min_ + ptp/2
                lower = middle - 0.05 * ptp
                upper = middle + 0.05 * ptp

                pts = None
                for axis in range(3):
                    if ptp[axis] == 0:
                        pass
                    else:
                        indices = (slice_pts[:,axis] < upper[axis]) * (slice_pts[:,axis] > lower[axis])
                        pts_ = slice_pts[indices, :]
                        if pts is None:
                            pts = pts_
                        else:
                            pts = np.append(pts, pts_, axis=0)
            #             pts.append()
            elif self.use_center_pts_only is False:
                pts = slice_.points

            centroid = np.mean(pts, axis=0)
            # append Y-position of that point to the `Ys` list
            centers.append(centroid)
        centers = np.asarray(centers)

        inertial_matrix, _ = pca_svd(centers.T)

        vector = inertial_matrix[:,0]
        
        self._vector = vector
    
    def fit(self):
        self.get_diaph_vector()
    
    @property
    def vector(self):
        return self._vector
    
    @property
    def diaph_points(self):
        return self._diaph_points
    
    @property
    def epiph_points(self):
        return self._epiph_points
    
    @property
    def cart_pts(self):
        return self._cart_pts
    
    @property
    def bone_pts(self):
        return self._bone_pts

Instance variables

var bone_pts
Expand source code
@property
def bone_pts(self):
    return self._bone_pts
var cart_pts
Expand source code
@property
def cart_pts(self):
    return self._cart_pts
var diaph_points
Expand source code
@property
def diaph_points(self):
    return self._diaph_points
var epiph_points
Expand source code
@property
def epiph_points(self):
    return self._epiph_points
var vector
Expand source code
@property
def vector(self):
    return self._vector

Methods

def fit(self)
Expand source code
def fit(self):
    self.get_diaph_vector()
def get_bone_cart_points(self)
Expand source code
def get_bone_cart_points(self):
    labels = vtk_to_numpy(self.femur.mesh.GetPointData().GetArray(self.labels_name))
    if (type(self.cart_label) == list) or (type(self.cart_label) == tuple):
        cart_indices = None
        for cart_label in self.cart_label:
            if cart_indices is None:
                cart_indices = labels == cart_label
            cart_indices += labels == cart_label
    else:
        cart_indices = labels == self.cart_label
    cart_ids = np.where(cart_indices)
    cart_pts = np.squeeze(self.femur.point_coords[cart_ids,:])
    bone_ids = np.where(labels == self.bone_label)
    bone_pts = np.squeeze(self.femur.point_coords[bone_ids,:])

    self._bone_pts = bone_pts
    self._cart_pts = cart_pts
def get_diaph_epiph_points(self)
Expand source code
def get_diaph_epiph_points(self):
    if (self.long_axis is None) or (self.proximal is None):
        self.get_long_axis_and_proximal_direction()
    if self.origin is None:
        self.guess_origin()
        
    # NORMAL MUST HAVE MAGNITUDE OF 1
    normal = np.zeros(3)
    normal[self.long_axis] = 1

    # create a random point on the plane we want to slice from. 
    point_on_plane = np.random.random_sample(size=3) * 20 - 10 # make random sample in range [-10, 10]
    point_on_plane[self.long_axis] = self.origin[self.long_axis]

    side = normal @ (self.femur.point_coords - point_on_plane).T

    diaph_points = self.femur.point_coords[side > 0, :]
    epiph_points = self.femur.point_coords[side < 0, :]
    
    pv_femur = pv.PolyData(self.femur.mesh)
    pv_diaph, _ = pv_femur.remove_points(side<0)
    
    self.pv_diaph = pv_diaph
    self._diaph_points = diaph_points
    self._epiph_points = epiph_points
def get_diaph_vector(self)
Expand source code
def get_diaph_vector(self):
    if (self._diaph_points is None) or (self._epiph_points is None) or (self.pv_diaph):
        self.get_diaph_epiph_points()            
    
    min_long = np.min(self._diaph_points[:, self.long_axis])
    max_long = np.max(self._diaph_points[:, self.long_axis])
    if self.proximal == 1:
        min_ = min_long + self.buffer
        max_ = max_long - self.buffer
    elif self.proximal == -1:
        max_ = max_long - self.buffer
        min_ = min_long + self.buffer

    normal = [0, 0, 0]
    normal[self.long_axis] = 1

    centers = []

    for idx, depth in enumerate(np.linspace(min_, max_, self.n_pts)):
        # get slice
        origin_ = [0, 0, 0]
        origin_[self.long_axis] = depth
        slice_ = self.pv_diaph.slice(normal=normal, origin=origin_)
        # get points from slice
        slice_pts = slice_.points

        if self.use_center_pts_only is True:
            ptp = np.ptp(slice_pts, axis=0)
            min_ = np.min(slice_pts, axis=0)
            middle = min_ + ptp/2
            lower = middle - 0.05 * ptp
            upper = middle + 0.05 * ptp

            pts = None
            for axis in range(3):
                if ptp[axis] == 0:
                    pass
                else:
                    indices = (slice_pts[:,axis] < upper[axis]) * (slice_pts[:,axis] > lower[axis])
                    pts_ = slice_pts[indices, :]
                    if pts is None:
                        pts = pts_
                    else:
                        pts = np.append(pts, pts_, axis=0)
        #             pts.append()
        elif self.use_center_pts_only is False:
            pts = slice_.points

        centroid = np.mean(pts, axis=0)
        # append Y-position of that point to the `Ys` list
        centers.append(centroid)
    centers = np.asarray(centers)

    inertial_matrix, _ = pca_svd(centers.T)

    vector = inertial_matrix[:,0]
    
    self._vector = vector
def get_long_axis_and_proximal_direction(self)
Expand source code
def get_long_axis_and_proximal_direction(self):
    if (self._bone_pts is None) or (self._cart_pts is None):
        self.get_bone_cart_points()
    if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
            self.get_min_max_bone_cart()

    cart_range = self.max_cart - self.min_cart
    bone_range = self.max_bone - self.min_bone
    long_axis = np.argmax(np.abs(cart_range - bone_range))

    if np.abs(self.max_cart[2] - self.max_bone[2]) > np.abs(self.min_cart[2] - self.min_bone[2]):
        proximal = int(1)
    else:
        proximal = int(-1)
    
    self.proximal = proximal
    self.long_axis = long_axis
def get_min_max_bone_cart(self)
Expand source code
def get_min_max_bone_cart(self):
    if (self._bone_pts is None) or (self._cart_pts is None):
        self.get_bone_cart_points()

    self.max_cart = np.max(self._cart_pts, axis=0)
    self.max_bone = np.max(self._bone_pts, axis=0)

    self.min_cart = np.min(self._cart_pts, axis=0)
    self.min_bone = np.min(self._bone_pts, axis=0)
def guess_origin(self)
Expand source code
def guess_origin(self):
    if (self.max_cart is None) or (self.max_bone is None) or (self.min_cart is None) or (self.min_bone is None):
            self.get_min_max_bone_cart()

    epiphysis_height = self.max_cart[self.long_axis] - self.min_cart[self.long_axis]

    origin = np.mean(self.femur.point_coords, axis=0)

    if self.proximal == 1:
        origin[self.long_axis] = self.max_cart[self.long_axis] + epiphysis_height * self.percent_epiph_higher # add 5mm buffer?    
    if self.proximal == -1:
        origin[self.long_axis] = self.min_cart[self.long_axis] - epiphysis_height * self.percent_epiph_higher # add 5mm buffer? 
    
    self.origin = origin