Module src.util.math_utils

Math utils.

Collection of mathematical functions used in various parts of this project.

Functions

def angle_between_vectors(u: numpy.ndarray, v: numpy.ndarray) ‑> float

Finds the angle between two vectors.

Args

u
An arbitrary dimensional vector.
v
An arbitrary dimensional vector.

Returns

Angle between the vectors in radians, domain
[0, pi]
def signed_angle(u: numpy.ndarray, v: numpy.ndarray, n: numpy.ndarray) ‑> float

Signed angle theta between vectors u and v given normal vector n.

\sin\theta={(\boldsymbol{n}\times\boldsymbol{u})\cdot\boldsymbol{v}\over |u||v|}. \theta = \begin{cases} \arccos{\frac{u\cdot v}{|u||v|}}, & \text{if $\sin\theta\ge0$;}\\ 2\pi-\arccos{\frac{u \cdot v}{|u||v|}}, & \text{if $\sin\theta<0$.}\\ \end{cases}

Args

u
An arbitrary dimensional vector.
v
An arbitrary dimensional vector.
n
Plane normal for vectors u & v.

Returns

Signed angle in radians between the two vectors u and v, given the orientational normal vector n. Domain [-pi, pi].

def to_unit_vector(v: numpy.ndarray) ‑> numpy.ndarray

Calculate unit vector corresponding to input vector.

Does not support zero vector.

Args

v
An arbitrary dimensional vector.

Returns

The unit vector corresponding to the vector v.

Raises

AssertionError
If the given vector is a zero vector.
def transform_length(v: numpy.ndarray, new_length: float) ‑> numpy.ndarray

Calculate a new vector parallell to the vector v, with length new_length.

Args

v
An arbitrary dimensional vector. Zero vector not supported.
new_length
The desired length for the returned vector. Must be non-negative.

Returns

A vector of same shape and direction as v, with magnitude equaling new length.

Raises

AssertionError
If the given v is a zero vector.
def landmark_to_vector(lm: mediapipe.framework.formats.landmark_pb2.NormalizedLandmark) ‑> numpy.ndarray

Convert landmark to a vector

Args

lm
A normalized landmark.

Returns

A 3d-vector corresponding to the input landmark.

def landmarks_to_vectors(lms: Iterable[mediapipe.framework.formats.landmark_pb2.NormalizedLandmark]) ‑> numpy.ndarray

Convert an iterable of landmarks to an array of corresponding vectors.

Args

lms
An iterable of normalized landmarks.

Returns

An array of vectors corresponding to the landmarks in lms.

def line_intersection(a1: numpy.ndarray, a2: numpy.ndarray, b1: numpy.ndarray, b2: numpy.ndarray) ‑> numpy.ndarray

Intersection between two lines from four points.

Constructs two lines in the xy-plane from a1 -> a2, b1 -> b2, and finds the intersection between these two.

Args

a1, a2: Two corresponding points as np.arrays of length 2, forms the first line. b1, b2: Two corresponding points as np.arrays of length 2, forms the second line.

Returns

The intersection point between the constructed lines as a np.array of length 2. Parallel lines generates the return value np.array([np.nan, np.nan]) (see below).

Raises

UserWarning
For a set of points that gives parallel lines (no intersection to be found).
def normalized_to_pixel_coordinates(normalized_x: float, normalized_y: float, width: int, height: int) ‑> Union[NoneType, Tuple[int, int]]

Converts a normalized value pair to pixel coordinates.

Args

normalized_x
x-coordinate, must be in domain [0, 1]
normalized_y
y-coordinate, must be in domain [0, 1]
width
Width of image.
height
Height of image.

Returns

An (x, y) tuple of pixel coordinates. Note that the max for the return values is one less than width / height.

def get_extreme_points(points: Iterable[numpy.ndarray], width: int, height: int) ‑> Tuple[Tuple[int, int], Tuple[int, int]]

Get corners of provided points.

Given an iterable of normalized points together with image width and height, returns pixel coordinates of the outer points.

Args

points
Normalized points of with x, y coordinates at 0th and 1st index respectively.
width
Width of the image that the return value should be fitted to.
height
Height of the image that the return values hould be fitted to.

Returns

A tuple of two tuples
(top_left, bot_right) = ((x_min, y_min), (x_max, y_max)).

These corners gives a bounding box surrounding the provided points. Note that origin is located at top left.

def centroid(triangle: Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]) ‑> numpy.ndarray

Calculate centroid of a triangle.

The centroid of a triangle is the point of intersection of its medians.

Args

triangle
Three-tuple of the triangle corners as numpy arrays. The arrays must have the same length >= 2

Returns

Centroid of the triangle as a np.ndarray.

def cube_root(n: Union[float, complex]) ‑> Union[float, complex]

Computes cubic root of a real/complex number while maintaining its sign

When raising negative numbers to fractional powers, which in essence is a multivalued function, python favors the priciple solution (that is the solution with the largest real part), which results in complex results for negative real inputs.

This is not what we want in this case though, we want the cube root of a real to stay real. Ex: cube_root(-1) = -1. This is easily achieved with this function.

def solve_general_cubic(a: float, b: float, sub: float) ‑> Tuple[complex, complex, complex]

Solves general cubic polynomial

Given a and b, the coefficients of a depressed cubic polynomial on the form t^3 + at + b, and the substitution used to obtain the depressed form, it will find the three roots of the polynomial.

By using either Cardano's formula or the trigonometric cubic equation, we garatuee three solutions. With cubics stemming from bezier curves we can further guaratee a real solution on [0, 1]^2.

Args

a
coefficient of the linear term
b
constant term
sub
the lambda value used for substitution when calculating the depressed form, t = u - lambda

Returns

A tuple of three roots of the equation, one is guaranteed to be real, the others are either complex or real.