code: def decompose_matrix(matrix):
      """Return sequence of transformations from transformation matrix.
  
      Code modified from the excellent work of Christoph Gohlke, link
      provided here:
      https://github.com/cgohlke/transformations/blob/master/transformations/transformations.py
  
      Parameters
      ----------
      matrix : array_like
          Non-degenerate homogeneous transformation matrix
  
      Returns
      -------
      scale : (3,) ndarray
          Three scaling factors.
      shear : (3,) ndarray
          Shear factors for x-y, x-z, y-z axes.
      angles : (3,) ndarray
          Euler angles about static x, y, z axes.
      translate : (3,) ndarray
          Translation vector along x, y, z axes.
      perspective : ndarray
          Perspective partition of matrix.
  
      Raises
      ------
      ValueError
          If matrix is of wrong type or degenerate.
  
      Examples
      --------
      >>> import numpy as np
      >>> T0=np.diag([2,1,1,1])
      >>> scale, shear, angles, trans, persp = decompose_matrix(T0)
  
      """
      M = np.array(matrix, dtype=np.float64, copy=True).T
      if abs(M[3, 3]) < _EPS:
          raise ValueError("M[3, 3] is zero")
      M /= M[3, 3]
      P = M.copy()
      P[:, 3] = 0, 0, 0, 1
      if not np.linalg.det(P):
          raise ValueError("matrix is singular")
  
      scale = np.zeros((3,), dtype=np.float64)
      shear = [0, 0, 0]
      angles = [0, 0, 0]
  
      if any(abs(M[:3, 3]) > _EPS):
          perspective = np.dot(M[:, 3], np.linalg.inv(P.T))
          M[:, 3] = 0, 0, 0, 1
      else:
          perspective = np.array((0, 0, 0, 1), dtype=np.float64)
  
      translate = M[3, :3].copy()
      M[3, :3] = 0
  
      row = M[:3, :3].copy()
      scale[0] = vector_norm(row[0])
      row[0] /= scale[0]
      shear[0] = np.dot(row[0], row[1])
      row[1] -= row[0] * shear[0]
      scale[1] = vector_norm(row[1])
      row[1] /= scale[1]
      shear[0] /= scale[1]
      shear[1] = np.dot(row[0], row[2])
      row[2] -= row[0] * shear[1]
      shear[2] = np.dot(row[1], row[2])
      row[2] -= row[1] * shear[2]
      scale[2] = vector_norm(row[2])
      row[2] /= scale[2]
      shear[1:] /= scale[2]
  
      if np.dot(row[0], np.cross(row[1], row[2])) < 0:
          scale *= -1
          row *= -1
  
      angles[1] = math.asin(-row[0, 2])
      if math.cos(angles[1]):
          angles[0] = math.atan2(row[1, 2], row[2, 2])
          angles[2] = math.atan2(row[0, 1], row[0, 0])
      else:
          # angles[0] = math.atan2(row[1, 0], row[1, 1])
          angles[0] = math.atan2(-row[2, 1], row[1, 1])
          angles[2] = 0.0
  
      return scale, shear, angles, translate, perspective
  
imports: import itertools
  import math
  import numpy
  import numpy.linalg
label_desc: Use image node_id 15 for calling a global function with example usage: decompose_matrix(matrix) and returns: scale,  shear,  angles,  translate,  perspective