Source code for pymapmanager.mmStackLine

from __future__ import print_function

import os, io, math
from errno import ENOENT
import pandas as pd
import numpy as np

[docs]class mmStackLine(): """ A stack line represents a 3D tracing of a number of dendritic segments. Each spineROI annotation in a mmStack is associated with one segmentID. Example:: from pymapmanager.mmMap import mmMap myMapFile = 'PyMapManager/examples/exampleMaps/rr30a/rr30a.txt' myMap = mmMap(filePath=myMapFile) # Get the (x,y,z) values, in um, of the 2nd mmStack tracing xyz = myMap.stacks[2].getLine() """ def __init__(self,stack): """ Load a line tracing for a stack. In addition to (x,y,z), the line has 'parentID' to specify multiple dendritic tracings Args: stack (:class:`pymapmanager.mmStack`): The stack that will own the line. """ self.stack = stack """ The parent stack """ self.linedb = None """ Pandas dataframe of line with (x,y,z,ID) columns. """ header = None if stack.urlmap is not None: # careful, we use tmp later to load (we need to know # header rows) tmp = self.stack.server.getfile('line', stack.urlmap, timepoint=stack.mapSession) header = tmp.split('\r')[0] else: if stack.map_: lineFile = stack._folder + 'line' + '/' + stack.name + '_l.txt' else: lineFile = stack._folder + 'stackdb' + '/' + stack.name + '_l.txt' #if not os.path.isfile(lineFile): # raise IOError(ENOENT, 'mmStackLine did not find lineFile:', lineFile) if os.path.isfile(lineFile): with open(lineFile, 'rU') as f: header = f.readline().rstrip() if header is not None: if header.endswith(';'): header = header[:-1] header = header.split(';') d = dict(s.split('=') for s in header) # line file has a header of segments # 1 file header + 1 segment header + numHeaderRow numHeaderRow = int(d['numHeaderRow']) startReadingRow = 1 + 1 + numHeaderRow if stack.urlmap is not None: self.linedb = pd.read_csv(io.StringIO(tmp.decode('utf-8')), header=startReadingRow, index_col=False) else: self.linedb = pd.read_csv(lineFile, header=startReadingRow, index_col=False)
[docs] def getLineValues3(self,pd): """ Args: pd (dict): See mmUtil.newplotdict() Returns: pd with ['x'], ['y'], and ['z'] values filled in as numpy ndarray """ pd['x'] = None pd['y'] = None pd['z'] = None # 20171222 adding sDist and ID for web client pd['sDist'] = None pd['ID'] = None if self.linedb is None: print('warning: getLineValues3() did not find a line') else: df = self.linedb if pd['segmentid'] >= 0: # we are passing pd['segmentid'] as an int df = df[df['ID'].isin([pd['segmentid']])] pd['x'] = df['x'].values pd['y'] = df['y'].values pd['z'] = df['z'].values # 20171222 adding sDist and ID for web client pd['sDist'] = df['sDist'].values pd['ID'] = df['ID'].values return pd
[docs] def getLine(self,segmentID=[]): """ Get the x/y/z values of a line tracing. Pass segmentID to get just one tracing. Note, x/y are in um, z is in slices! Args: segmentID (list): List of int specifying which segmentID, pass [] to get all. Return: numpy ndarray of (x,y,z) """ if self.linedb is None: return None df = self.linedb if segmentID: df = df[df['ID'].isin(segmentID)] ret = df[['x','y','z']].values return ret
def _EuclideanDist(self, from_xyz, to_xyz): """ Get the euclidean distance between two points, pass tuple[2]=np.nan to get 2d distance Args: from_xyz (3 tuple): to_xyz (3 tuple): Returns: float """ if from_xyz[2] and to_xyz[2]: ret = math.sqrt(math.pow(abs(from_xyz[0]-to_xyz[0]),2) \ + math.pow(abs(from_xyz[1]-to_xyz[1]),2) \ + math.pow(abs(from_xyz[2]-to_xyz[2]),2)) else: ret = math.sqrt(math.pow(abs(from_xyz[0]-to_xyz[0]),2) \ + math.pow(abs(from_xyz[1]-to_xyz[1]),2)) return ret
[docs] def getLineLength(self, segmentID, smoothz=None): """Get the 3D line length for one segment (um) Args: segmentID (int): Stack centric segmentID, different from other functions, requires an int (not a list) smoothz (int): Smooth Z Returns: 3D length (float) of segmentID """ # strip down df df = self.linedb df = df[df['ID'].isin(segmentID)] # grab x/y/z values (z is in slices!!!) values = df[['x', 'y', 'z']].values # convert z to microns values[:,2] *= self.stack.voxelz # slices * um/slice -->> um # filter z if smoothz: pass # step through rows and get euclidean distance between each row i and row i-1 dist = 0.0 prev_xyz = (np.nan, np.nan, np.nan) for i, row in enumerate(values): this_xyz = (row[0], row[1], row[2]) if i>0: dist += self._EuclideanDist(prev_xyz, this_xyz) prev_xyz = tuple(this_xyz) return dist