code: def mat2euler(M, cy_thresh=None):
      """Discover Euler angle vector from 3x3 matrix
  
      Uses the conventions above.
  
      Parameters
      ----------
      M : array-like, shape (3,3)
      cy_thresh : None or scalar, optional
         threshold below which to give up on straightforward arctan for
         estimating x rotation.  If None (default), estimate from
         precision of input.
  
      Returns
      -------
      z : scalar
      y : scalar
      x : scalar
         Rotations in radians around z, y, x axes, respectively
  
      Notes
      -----
      If there was no numerical error, the routine could be derived using
      Sympy expression for z then y then x rotation matrix, which is::
  
        [                       cos(y)*cos(z),                       -cos(y)*sin(z),         sin(y)],
        [cos(x)*sin(z) + cos(z)*sin(x)*sin(y), cos(x)*cos(z) - sin(x)*sin(y)*sin(z), -cos(y)*sin(x)],
        [sin(x)*sin(z) - cos(x)*cos(z)*sin(y), cos(z)*sin(x) + cos(x)*sin(y)*sin(z),  cos(x)*cos(y)]
  
      with the obvious derivations for z, y, and x
  
         z = atan2(-r12, r11)
         y = asin(r13)
         x = atan2(-r23, r33)
  
      Problems arise when cos(y) is close to zero, because both of::
  
         z = atan2(cos(y)*sin(z), cos(y)*cos(z))
         x = atan2(cos(y)*sin(x), cos(x)*cos(y))
  
      will be close to atan2(0, 0), and highly unstable.
  
      The ``cy`` fix for numerical instability below is from: *Graphics
      Gems IV*, Paul Heckbert (editor), Academic Press, 1994, ISBN:
      0123361559.  Specifically it comes from EulerAngles.c by Ken
      Shoemake, and deals with the case where cos(y) is close to zero:
  
      See: http://www.graphicsgems.org/
  
      The code appears to be licensed (from the website) as "can be used
      without restrictions".
      """
      M = np.asarray(M)
      if cy_thresh is None:
          try:
              cy_thresh = np.finfo(M.dtype).eps * 4
          except ValueError:
              cy_thresh = _FLOAT_EPS_4
      r11, r12, r13, r21, r22, r23, r31, r32, r33 = M.flat
      # cy: sqrt((cos(y)*cos(z))**2 + (cos(x)*cos(y))**2)
      cy = math.sqrt(r33 * r33 + r23 * r23)
      if cy > cy_thresh:  # cos(y) not close to zero, standard form
          z = math.atan2(
              -r12, r11
          )  # atan2(cos(y)*sin(z), cos(y)*cos(z))
          y = math.atan2(r13, cy)  # atan2(sin(y), cy)
          x = math.atan2(
              -r23, r33
          )  # atan2(cos(y)*sin(x), cos(x)*cos(y))
      else:  # cos(y) (close to) zero, so x -> 0.0 (see above)
          # so r21 -> sin(z), r22 -> cos(z) and
          z = math.atan2(r21, r22)
          y = math.atan2(r13, cy)  # atan2(sin(y), cy)
          x = 0.0
      return z, y, x
  
file_path: AdvBox/advbox_family/ODD/EOT_simulation/eulerangles.py
imports: import math
  import sys
  import numpy
label_desc: Use image node_id 2 for calling a global function with example usage: mat2euler(M, cy_thresh) and returns: z,  y,  x