Source code for monalisa.trajectory._traj_n_line

"""Port of src/traj123/bmTraj_nLine.m."""

import numpy as np

from monalisa.array._point_reshape import point_reshape


def _float_dtype(t):
    return t.dtype if t.dtype in (np.float32, np.float64) else np.float64


[docs] def traj_n_line(argTraj, nargout=1): """Detect the number of straight lines of a trajectory. Returns ``nLine`` or, for ``nargout > 1``, ``(nLine, N, isN_integer, dK_list)`` truncated to ``nargout`` values, with ``N = nPt / nLine`` points per line. """ t = np.asarray(point_reshape(argTraj)) dt = _float_dtype(t) t = t.astype(dt, copy=False) th_n1 = 3.5 th_n_de = 1 / 1000 myEps = 10 * np.finfo(np.float64).eps delta_separation = myEps / (th_n_de / 1000) imDim, nPt = t.shape d1 = np.concatenate([np.zeros((imDim, 1), dtype=dt), t[:, 1:] - t[:, :-1]], axis=1) n1 = np.zeros(nPt, dtype=dt) for i in range(imDim): n1 = n1 + d1[i, :] ** 2 n1 = np.sqrt(n1) n1[0] = 0 jump_mask = n1 > th_n1 * np.median(n1) nonSeparated_mask = n1 <= delta_separation n1[nonSeparated_mask] = 1 d1[:, nonSeparated_mask] = 0 nonSeparated_mask = nonSeparated_mask | np.roll(nonSeparated_mask, -1) e = d1 / n1[None, :] de = e[:, 1:] - e[:, :-1] de[:, 0] = 0 de = np.concatenate([de, np.zeros((imDim, 1), dtype=dt)], axis=1) n_de = np.zeros(nPt, dtype=dt) for i in range(imDim): n_de = n_de + de[i, :] ** 2 n_de = np.sqrt(n_de) dirChange_mask = n_de > th_n_de outOfLine_mask = jump_mask | dirChange_mask | nonSeparated_mask inLine = ~outOfLine_mask[1:] previous = np.concatenate([[True], outOfLine_mask[1:-1]]) nLine = float(np.sum(inLine & previous)) dK_list = n1[1:][inLine][None, :] with np.errstate(divide="ignore", invalid="ignore"): N = np.float64(nPt) / np.float64(nLine) isN_integer = not (abs(N - np.round(N)) > 0) if nargout <= 1: return nLine return (nLine, N, isN_integer, dK_list)[:nargout]