Source code for TIE.TIE_core
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 23 09:19:29 2021
@author: Anna Rauch
This script contains a set of general functions/methods that perform the
core-steps of the TIE-method (i.e. the actual TIE-steps)
"""
import copy
from typing import Dict, List, Tuple, Union
import numpy as np
from TIE import TIE_classes as TIEclass
from TIE import TIE_general as TIEgen
[docs]
def classifyTRACE(TRACES, pth=None):
"""Classify traces based on TIE parameters.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects to be classified.
pth : list of float or None, optional
Planarity thresholds for classification. If None, default thresholds
[3, 9, 18] will be used. Must be of size 3.
Returns
-------
list of trace objects (TIE_classes.trace_OBJ)
The input list of trace objects with added classification parameters.
"""
if pth is None:
pth = [3, 9, 18]
p1 = pth[0]
p2 = pth[1]
p3 = pth[2]
for T in TRACES:
Seg = T.Segment
for S in Seg:
aN = np.array(
[
S.Chords[ci].alpha[0]
for ci in range(np.size(S.Chords))
if np.isnan(S.Chords[ci].alpha[0]) == False
]
).squeeze()
aR = np.array(
[
S.Chords[ci].alpha[1]
for ci in range(np.size(S.Chords))
if np.isnan(S.Chords[ci].alpha[1]) == False
]
).squeeze()
bN = np.array(
[
S.Chordplanes[cpi].beta[0]
for cpi in range(np.size(S.Chordplanes))
if np.isnan(S.Chordplanes[cpi].beta[0]) == False
]
).squeeze()
bR = np.array(
[
S.Chordplanes[cpi].beta[1]
for cpi in range(np.size(S.Chordplanes))
if np.isnan(S.Chordplanes[cpi].beta[1]) == False
]
).squeeze()
alpS = np.sum(aN + aR)
alpD = np.abs(np.sum(aN) - np.sum(aR))
alp = alpS - alpD
meana = alp / np.size(S.Chords)
betS = np.sum(bN + bR)
betD = np.abs(np.sum(bN) - np.sum(bR))
bet = betS - betD
meanb = bet / np.size(S.Chordplanes)
if meanb == 0:
meanb = 0.1
meanr = meana / meanb
fcurb1b = 180 / (meana - p1) + p1
fcurb2b = 180 / (meana - p2) + p2
fcurb3b = 180 / (meana - p3) + p3
if meana < p1:
g = 1
elif meanb <= fcurb1b:
g = 1
elif meana < p2:
g = 2
elif meanb <= fcurb2b:
g = 2
elif meana < p3:
g = 3
elif meanb <= fcurb3b:
g = 3
elif meanb > fcurb3b:
g = 4
if meanr < 1:
S.classID = -g
else:
S.classID = g
S.sigheight = [meana, meanb]
T.Segment = Seg
return TRACES
[docs]
def changeTHseg(TRACES, X, Y, Z, n, amp=-10, newTH=None):
"""
Change Threshold For Segmentation.
Changes the threshold that defines the convexity size to segment the traces
for a specific trace n and performs the TIE on new segmentation.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
X, Y, Z : array_like
Coordinate vectors.
n : int
Trace ID of the trace that is going to be analyzed with changed threshold.
amp : float, optional
Amplitude expressed in % by which the defined threshold should be
lowered (negative) or increased (positive). Default is -10.
newTH : list or None, optional
Newly defined convexity size threshold [peak prominence, peak width].
If newTH is defined, the amplitude is ignored. Default is None.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with adapted segmentation for the specific trace object.
"""
if newTH is None:
newTH = [] # Initialize the list here if it's None
formthresh = TRACES[n - 1].convexityTH
amp = (100 + amp) / 100
if np.size(newTH) > 0:
pthresh = newTH
else:
pthresh = [formthresh[0] * amp, formthresh[1] / amp]
TRACES = clearTIE(TRACES, [n])
TRACESn = tie(
[TRACES[n - 1]], X, Y, Z, pth=[3, 9, 18], seg=True, peakthresh=pthresh
)
TRACES[n - 1] = TRACESn[0]
TRACES[n - 1].convexityTH = pthresh
return TRACES
[docs]
def clearTIE(TRACES, n=None):
"""
Clear TIE information from trace objects.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
n : list or None, optional
List of trace IDs (integers) that designate the traces to be cleared.
If n is None (default), all traces will be cleared.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with suppressed TIE information.
"""
if n is None:
n = [] # Initialize the list here if it's None
if np.size(n) == 0:
n = np.arange(0, int(np.size(TRACES))).astype(int)
else:
n = [ni - 1 for ni in n]
for T in [TRACES[ni] for ni in n]:
ltr = np.size(T.index)
i_ind = np.arange(0, ltr).astype(int)
seg = TIEclass.segment_OBJ(1, [], i_ind, i_ind[::-1], [], [], [], [])
T.Segment = [seg]
T.orientbar = []
T.convexityTH = float("nan")
return TRACES
[docs]
def extractAlpha(TRACES):
"""
Extracts alpha signal from traces.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with added alpha values for each trace object (respectively
each chord object).
"""
for T in TRACES:
Segment = T.Segment
for S in Segment:
Chords = S.Chords
n2 = np.size(Chords)
vIniN = Chords[0].vector[0] # initial vect for normal signal
vIniR = Chords[0].vector[1] # initial vect for reverse signal
for i in range(n2):
vAnaN = Chords[i].vector[0] # analized vect for normal signal
vAnaR = Chords[i].vector[1] # analized vect for reverse signal
angleN = TIEgen.angleBtwVec(vAnaN, vIniN)
angleR = TIEgen.angleBtwVec(vAnaR, vIniR)
Chords[i].alpha = [angleN, angleR]
S.Chords = Chords
T.Segment = Segment
return TRACES
[docs]
def extractBeta(TRACES):
"""
Extracts beta signal from traces.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with added beta values for each trace object (respectively
each chordPlane object).
"""
for T in TRACES:
Segment = T.Segment
for S in Segment:
ChdPlane = S.Chordplanes
refpN = ChdPlane[0].normal[0]
refpN = refpN / np.linalg.norm(refpN)
refpR = ChdPlane[0].normal[1]
refpR = refpR / np.linalg.norm(refpR)
betN = [TIEgen.angleBtwVec(CP.normal[0], refpN) for CP in ChdPlane]
betR = [TIEgen.angleBtwVec(CP.normal[1], refpR) for CP in ChdPlane]
for j in range(1, np.size(betN)):
if np.isnan(betN[j]) == False:
if betN[j] - betN[j - 1] > 90:
betN[j] = np.abs(betN[j] - 180)
betN[j] = betN[j]
for j in range(1, np.size(betR)):
if np.isnan(betR[j]) == False:
if betR[j] - betR[j - 1] > 90:
betR[j] = np.abs(betR[j] - 180)
betR[j] = betR[j]
for i in range(np.size(betN)):
ChdPlane[i].beta = [betN[i], betR[i]]
S.Chordplanes = ChdPlane
T.Segment = Segment
return TRACES
[docs]
def extractChdPlanes(TRACES, mX, mY, mZ):
"""
Extracts chord planes from traces.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
mX, mY, mZ : array-like
Matrices of coordinates of the analyzed extent.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with added list of chord plane objects (TIE_classes.chordPlane_OBJ)
to each trace object (respectively each segment object).
"""
mXfl = mX.flatten()
mYfl = mY.flatten()
mZfl = mZ.flatten()
for T in TRACES:
index = (T.index).astype(int)
Segment = T.Segment
tracel = np.zeros(np.size(index) - 1)
for i in range(np.size(tracel)):
PP1 = [mXfl[index[i]], mYfl[index[i]], mZfl[index[i]]]
PP2 = [mXfl[index[i + 1]], mYfl[index[i + 1]], mZfl[index[i + 1]]]
tracel[i] = TIEgen.distance(PP1, PP2)
tr_length_sum = np.sum(tracel)
for S in Segment:
Chords = S.Chords
N = np.size(Chords)
if N % 2 == 0:
step = int(N / 2)
steppoint = step
else:
step = int((N - 1) / 2)
steppoint = step + 1
indexsegN = T.index[S.ind_normal].astype(int) # Normal signal
indexsegR = T.index[S.ind_reverse].astype(int) # Reverse signal
ChdPlane = []
for i in range(steppoint):
v1N = Chords[i].vector[0]
v2N = Chords[i + step].vector[0]
v1R = Chords[i].vector[1]
v2R = Chords[i + step].vector[1]
normalN = np.cross(v1N, v2N)
azimN, dipN = TIEgen.normal2angle(normalN)
normalR = np.cross(v1R, v2R)
azimR, dipR = TIEgen.normal2angle(normalR)
poleplN = 90 - dipN
poletrN = azimN - 180
if poletrN < 0:
poletrN = 360 + poletrN
poleplR = 90 - dipR
poletrR = azimR - 180
if poletrR < 0:
poletrR = 360 + poletrR
P1N = np.array(
[mXfl[indexsegN[i]], mYfl[indexsegN[i]], mZfl[indexsegN[i]]]
)
P2N = np.array(
[
mXfl[indexsegN[i + step]],
mYfl[indexsegN[i + step]],
mZfl[indexsegN[i + step]],
]
)
P1P2N = P2N - P1N
distN = np.abs(np.dot(np.cross(v1N, v2N), P1P2N)) / np.linalg.norm(
np.cross(v1N, v2N)
)
distratioN = distN / tr_length_sum
P1R = np.array(
[mXfl[indexsegR[i]], mYfl[indexsegR[i]], mZfl[indexsegR[i]]]
)
P2R = np.array(
[
mXfl[indexsegR[i + step]],
mYfl[indexsegR[i + step]],
mZfl[indexsegR[i + step]],
]
)
P1P2R = P2R - P1R
distR = np.abs(np.dot(np.cross(v1R, v2R), P1P2R)) / np.linalg.norm(
np.cross(v1R, v2R)
)
distratioR = distR / tr_length_sum
ChdPlane.append(
TIEclass.chordPlane_OBJ(
i + 1,
[normalN, normalR],
[azimN, dipN, azimR, dipR],
[poletrN, poleplN, poletrR, poleplR],
[],
[distN, distR],
[distratioN, distratioR],
)
)
S.Chordplanes = ChdPlane
T.Segment = Segment
return TRACES
[docs]
def extractChords(TRACES, mX, mY, mZ):
"""
Extracts connecting chords of a trace set (1st step of TIE).
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
mX, mY, mZ : array-like
Matrices of coordinates of the analyzed extent.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with added list of chords (TIE_classes.chords_OBJ)
to each trace object (respectively each segment object).
"""
mXfl = mX.flatten()
mYfl = mY.flatten()
mZfl = mZ.flatten()
for T in TRACES:
indtr = (T.index).astype(int)
Segment = T.Segment
for S in Segment:
l = np.size(S.ind_normal)
if l % 2 > 0:
DELTA = int((l - 1) / 2)
lstr = DELTA + 1
else:
DELTA = int(l / 2)
lstr = DELTA
if lstr % 2 > 0:
DD = int((lstr - 1) / 2)
else:
DD = int(lstr / 2)
S.delta = [DELTA, DD]
# normal trace
chordsN = np.zeros([lstr, 3])
trendN = np.zeros([lstr, 1])
plungeN = np.zeros([lstr, 1])
i = np.arange(0, lstr)
pti = S.ind_normal[i]
ptf = S.ind_normal[i + DELTA]
chordsN[i, 0] = mXfl[indtr[pti]] - mXfl[indtr[ptf]]
chordsN[i, 1] = mYfl[indtr[pti]] - mYfl[indtr[ptf]]
chordsN[i, 2] = mZfl[indtr[pti]] - mZfl[indtr[ptf]]
# Reverse trace
chordsR = np.zeros([lstr, 3])
trendR = np.zeros([lstr, 1])
plungeR = np.zeros([lstr, 1])
i = np.arange(0, lstr)
pti = S.ind_reverse[i]
ptf = S.ind_reverse[i + DELTA]
chordsR[i, 0] = mXfl[indtr[pti]] - mXfl[indtr[ptf]]
chordsR[i, 1] = mYfl[indtr[pti]] - mYfl[indtr[ptf]]
chordsR[i, 2] = mZfl[indtr[pti]] - mZfl[indtr[ptf]]
Chords = []
for chd in range(lstr):
trendN[chd], plungeN[chd] = TIEgen.vect2angle(chordsN[chd, :])
trendR[chd], plungeR[chd] = TIEgen.vect2angle(chordsR[chd, :])
Chords.append(
TIEclass.chord_OBJ(
chd + 1,
[chordsN[chd, :], chordsR[chd, :]],
[trendN[chd], trendR[chd]],
[plungeN[chd], plungeR[chd]],
[],
)
)
S.Chords = Chords
T.Segment = Segment
return TRACES
[docs]
def extractOrientBars(TRACES):
"""
Extraction of orientation bars (TIE).
Extracts orientation bars from chord planes. Chord plane values are
distributed along the trace.
Parameters
----------
TRACES : list of trace_OBJ
List of trace objects containing basic TRACE information
(any TRACE set, could also be FAULTS).
Returns
-------
TRACES : list of trace_OBJ
List with added orientation bar values to each trace object.
"""
for T in TRACES:
Segment = T.Segment
or_new = np.zeros([np.size(T.index), 3])
for S in Segment:
bet = [S.Chordplanes[bi].beta[0] for bi in range(np.size(S.Chordplanes))]
betI = [S.Chordplanes[bi].beta[1] for bi in range(np.size(S.Chordplanes))]
if np.sum(bet) > np.sum(betI):
orp = [
S.Chordplanes[bi].plane_orient[2:]
for bi in range(np.size(S.Chordplanes))
]
si = S.ind_reverse
else:
orp = [
S.Chordplanes[bi].plane_orient[:2]
for bi in range(np.size(S.Chordplanes))
]
si = S.ind_normal
d = S.delta[0]
dd = S.delta[1]
for k in range(len(orp)):
v1 = np.array(TIEgen.angle2vect(orp[k][0], orp[k][1])).squeeze()
anchor_a = d + k + 1
anchor_b = dd + d + k + 1
vanch_a = slice(k, anchor_a)
vanch_b = slice(dd + k, anchor_b)
for a in si[vanch_a]:
or_new[a, :] = or_new[a, :] + v1
for b in si[vanch_b]:
or_new[b, :] = or_new[b, :] + v1
or_new = [m / np.linalg.norm(m) for m in or_new]
for m in range(len(or_new)):
if or_new[m][2] > 0:
or_new[m] = or_new[m] * -1
T.orientbar = or_new
return TRACES
[docs]
def mergeSeg(TRACES, n, s, X, Y, Z):
"""
Merges two or more segments of a certain trace (trace_OBJ) into one single
segment and reclassifies it according to TIE.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
n : int
ID of specific trace_OBJ (only one trace_OBJ at a time).
s : list
List of segments that will be merged (e.g., [2, 3]).
X, Y, Z : array-like
Coordinate vectors.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with merged segments in the specific trace_OBJ.
"""
s = [i - 1 for i in s]
s.sort()
for i in range(len(s) - 1):
if abs(s[i] - s[i + 1]) > 1:
TIEgen.Mbox("ERROR", "trace segments are not adjacent!!", 1)
si = []
# FIXME: list index out of range exception
seglist = [TRACES[n - 1].Segment[ii] for ii in s]
for k in seglist:
si.extend(k.ind_normal.tolist())
si = np.unique(si)
SEG = TRACES[n - 1].Segment
SEG[s[0]].ind_normal = si
SEG[s[0]].ind_reverse = si[::-1]
for i in range(1, len(s)):
SEG.pop(int(s[i]))
for sii in range(len(SEG)):
SEG[sii].id = sii + 1
TRACES[n - 1].Segment = SEG
trace = tie([TRACES[n - 1]], X, Y, Z)
TRACES[n - 1] = trace[0]
TRACES[n - 1].convexityTH.append("merged " + str(s[0]) + " to " + str(s[-1]))
return TRACES
[docs]
def mergeTraces(TRACES, tn, X, Y, Z, seg=False):
"""
Merges two or more traces into one single trace object
and reclassifies it according to TIE.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
tn : list
List of trace IDs that will be merged (e.g., [2, 3]).
X, Y, Z : array-like
Coordinate vectors.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with merged traces (new IDs).
"""
tni = [ti - 1 for ti in tn]
ni = []
trlist = [TRACES[ii] for ii in tni]
for k in trlist:
ni.extend(k.index.tolist())
ni = np.unique(ni).astype(int)
ind_sort = TIEgen.sortLine(ni, np.shape(Z))
TRACES[tni[0]].index = ind_sort.astype(int)
TRACES = clearTIE(TRACES, [tn[0]])
trace = tie([TRACES[tni[0]]], X, Y, Z, seg=seg)
TRACES[tni[0]] = trace[0]
for tri in range(len(TRACES)):
if TRACES[tri].id in tn[1:]:
TRACES[tri] = 1
while 1 in TRACES:
TRACES.remove(1)
for tri in range(len(TRACES)):
TRACES[tri].id = tri + 1
return TRACES
[docs]
def segSeg(TRACES, n, s, X, Y, Z):
"""
Analyses a certain trace segment for potential segmentation at a
more detailed scale. Important for long irregular traces that were not
segmented during the first segmentation process.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
n : int
ID of the trace object that contains the segment.
s : int
ID of the segment that will be re-analyzed for segmentation.
X, Y, Z : array-like
Coordinate vectors.
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with potentially added segments.
"""
n = n - 1
s = s - 1
si = TRACES[n].Segment[s].ind_normal
ni = TRACES[n].index[si].astype(int)
TRACEX = copy.deepcopy(TRACES[n])
TRACEX.index = copy.copy(ni)
TRACEX = clearTIE([TRACEX])
TRACEX = tie(TRACEX, X, Y, Z, seg=True)
ls = len(TRACES[n].Segment)
lsx = len(TRACEX[0].Segment)
new_ls = ls + lsx - 1
rest_i = np.arange(s + lsx, new_ls)
new_i = np.arange(s, new_ls - np.size(rest_i))
Seg = TRACES[n].Segment
SegX = copy.deepcopy(TRACEX[0].Segment)
Seg.pop(s)
for k in range(np.size(new_i)):
si_old = SegX[k].ind_normal
si_new = si[SegX[k].ind_normal]
Seg.insert(new_i[k], copy.deepcopy(SegX[k]))
Seg[new_i[k]].ind_normal = si_new
Seg[new_i[k]].ind_reverse = si_new[::-1]
Seg[new_i[k]].id = new_i[k] + 1
ind_slice = slice(si_new[0], si_new[-1] + 1, 1)
ind_sliceX = slice(si_old[0], si_old[-1] + 1, 1)
TRACES[n].orientbar[ind_slice] = TRACEX[0].orientbar[ind_sliceX]
for k in rest_i:
Seg[k].id = k + 1
return TRACES
[docs]
def segmentTRACE(TRACES, mX, mY, mZ, peakthresh=[100, 15]):
"""
Segments the traces at their inflection points defined based on
a peak threshold.
Parameters
----------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List of trace objects.
mX, mY, mZ : array-like
Matrices of coordinates of the analyzed extent.
peakthresh : list of float, optional
Peak thresholds for convexity [peak prominence, peak width].
Default is [100, 15].
Returns
-------
TRACES : list of trace objects (TIE_classes.trace_OBJ)
List with added segmentation where needed: list of
segment objects (TIE_classes.segment_OBJ). Where no segmentation occurred,
only one segment for one trace exists.
"""
import scipy
mXfl = mX.flatten()
mYfl = mY.flatten()
mZfl = mZ.flatten()
for T in TRACES:
ind = (T.index).astype(int)
n = np.size(ind)
d = 2 # steps between connecting points to create a vector v
l = n - d # final vector length of connected paths
v = np.zeros([l, 3])
angle = np.zeros([l, 1])
if n > 5:
k = np.arange(0, l)
v[k, 0] = mXfl[ind[k + d]] - mXfl[ind[k]]
v[k, 1] = mYfl[ind[k + d]] - mYfl[ind[k]]
v[k, 2] = mZfl[ind[k + d]] - mZfl[ind[k]]
angle = np.array([TIEgen.angleBtwVec(v[m, :], v[0, :]) for m in range(l)])
angle = angle * np.pi / 180
anmean1 = angle.copy()
anmean2 = angle.copy()
m = slice(1, l)
p = slice(0, l - 1)
anmean1[m] = (angle[m] + angle[p]) / 2
anmean2[p] = (angle[m] + angle[p]) / 2
# signal smoothing
if n % 4 > 0:
smo = int((n - (n % 4)) / 2)
else:
smo = int(n / 2)
P = 1
while P < smo:
anmean1[m] = (anmean1[m] + anmean1[p]) / 2
anmean2[p] = (anmean2[m] + anmean2[p]) / 2
P = P + 1
anmean = (
np.cos(
np.concatenate(
(anmean1[int(smo / 2) :], anmean2[-smo : int(-smo / 2)])
)
)
* n
)
prom = peakthresh[0] # minimal peak prominence
width = n / peakthresh[1] # minimal peak width
# find signal peaks (positive and negative)
ppi = scipy.signal.find_peaks(anmean, prominence=prom, width=width)
npi = scipy.signal.find_peaks(
-anmean + np.max(anmean), prominence=prom, width=width
)
pind = np.sort(np.concatenate(([0], ppi[0], npi[0], [l - 1])))
pval = anmean[pind]
# with each positive peak must follow a negative one -> peaks within peaks
if np.size(pval) > 2:
ploc = scipy.signal.find_peaks(pval)[0]
nloc = scipy.signal.find_peaks(-pval)[0]
else:
ploc = []
nloc = []
seg = np.sort(np.concatenate((pind[ploc], pind[nloc])))
# peak width also at the beginning and the end of the signal
if np.size(seg) > 0:
if seg[0] < 50:
if np.size(seg) > 1:
seg = seg[1:]
else:
seg = []
if np.size(seg) > 0:
if l - seg[-1] < 50:
if np.size(seg) > 1:
seg = seg[:-1]
else:
seg = []
if np.size(seg) > 0:
ind_normal = np.arange(0, seg[0] + d)
T.Segment[0].ind_normal = ind_normal
T.Segment[0].ind_reverse = ind_normal[::-1]
if np.size(seg) > 1:
for s in range(1, np.size(seg)):
New_S = TIEclass.segment_OBJ(s + 1, [], [], [], [], [], [], [])
ind_normal = np.arange(seg[s - 1], seg[s] + d)
New_S.ind_normal = ind_normal
New_S.ind_reverse = ind_normal[::-1]
T.Segment.append(New_S)
Last_S = TIEclass.segment_OBJ(
np.size(seg) + 1, [], [], [], [], [], [], []
)
ind_normal = np.arange(seg[-1], n)
Last_S.ind_normal = ind_normal
Last_S.ind_reverse = ind_normal[::-1]
T.Segment.append(Last_S)
T.convexityTH = peakthresh
return TRACES
[docs]
def tie(
TRACES: List[TIEclass.trace_OBJ],
X: np.ndarray,
Y: np.ndarray,
Z: np.ndarray,
pth: List[int] = [3, 9, 18],
seg: bool = False,
peakthresh: List[int] = [100, 15],
) -> List[TIEclass.trace_OBJ]:
"""
Performs the entire TIE analysis, step by step.
Parameters
----------
TRACES : list of trace_OBJ (TIE_classes.trace_OBJ)
List of trace objects for TIE analysis.
X, Y : numpy.ndarray
Vectors of X and Y coordinates of the analyzed extent.
Z : numpy.ndarray
Matrix of Z values of the analyzed extent (size [len(X), len(Y)]).
pth : list of int, optional
Planarity thresholds for TIE classification. Default is [3, 9, 18].
seg : bool, optional
Boolean indicating if the trace should first undergo a segmentation
process before extracting the trace information. Default is False.
peakthresh : list of int, optional
Peak thresholds for convexity [peak prominence, peak width]. Default
is [90, 15], and it is not used if seg=False.
Returns
-------
TRACES : list of trace_OBJ
The same list with added TIE information.
"""
[mX, mY] = np.meshgrid(X, Y)
mX = np.fliplr(mX)
mZ = np.fliplr(Z)
if seg == True:
TRACES = segmentTRACE(TRACES, mX, mY, mZ, peakthresh)
TRACES = extractChords(TRACES, mX, mY, mZ)
TRACES = extractAlpha(TRACES)
TRACES = extractChdPlanes(TRACES, mX, mY, mZ)
TRACES = extractBeta(TRACES)
TRACES = classifyTRACE(TRACES, pth)
TRACES = extractOrientBars(TRACES)
return TRACES