28
$\begingroup$

How can I extract rotation and scale values from a 2D transformation matrix?

matrix = [1, 0, 0, 1, 0, 0]

matrix.rotate(45 / 180 * PI)
matrix.scale(3, 4)
matrix.translate(50, 100)
matrix.rotate(30 / 180 * PI)
matrix.scale(-2, 4)

Now my matrix have values [a, b, c, d, tx, ty]. Lets forget about the processes above and imagine that we have only the values a, b, c, d, tx, and ty. How can I find final rotation and scale values?

$\endgroup$

3 Answers 3

49
$\begingroup$

Essentially you need to solve the following

$$\left[\begin{array}{ccc} \mathrm{a} & \mathrm{b} & \mathrm{tx}\\ \mathrm{c} & \mathrm{d} & \mathrm{ty}\end{array}\right]=\left[\begin{array}{ccc} s_{x}\cos\psi & -s_{x}\sin\psi & x_{c}\\ s_{y}\sin\psi & s_{y}\cos\psi & y_{c}\end{array}\right]$$

where $s_x$, $s_y$ are the scalings, $x_c$, $y_c$ is the translation and $\psi$ is the rotation angle. The results I get are:

$$x_{c}=\mathrm{tx}$$ $$y_{c}=\mathrm{ty}$$ $$s_{x}=\mathrm{sign(a)\,}\sqrt{\mathrm{a}^{2}+\mathrm{b}^{2}}$$ $$s_{y}=\mathrm{sign(d)\,}\sqrt{\mathrm{c}^{2}+\mathrm{d}^{2}}$$ $$\tan\psi=-\frac{\mathrm{b}}{\mathrm{a}}=\frac{\mathrm{c}}{\mathrm{d}}$$

So the angle is either $\psi = {\rm atan2}(-b,a)$ or $\psi = {\rm atan2}(c,d)$

$\endgroup$
15
  • 1
    $\begingroup$ thank you but sx and sy will always be positive because of power 2. what about negative scale values? $\endgroup$
    – Tolgahan Albayrak
    Dec 5, 2010 at 22:20
  • 1
    $\begingroup$ You can check the sign of the scalings by looking at the sign of $\mathrm{a}$ for $s_x$ and the sign of $\mathrm{d}$ for $s_y$, since the $\cos\psi$ operation is not expected to produce a negative number for $-\pi/2\leq\psi\leq\pi/2$ which is what is returned by the $\arctan$ function. $\endgroup$ Dec 5, 2010 at 22:29
  • 1
    $\begingroup$ See edit now... $\endgroup$ May 6, 2014 at 12:44
  • 1
    $\begingroup$ Scaling one direction negative and rotating by 180° are equivalent operations when looking at only one point. You will need more that one point to discern rigid body motion. $\endgroup$ May 7, 2014 at 12:49
  • 2
    $\begingroup$ So the implementation of the rotation matrix may not be $\left[\begin{array}{ccc} s_{x}\cos\psi & -s_{x}\sin\psi & x_{c}\\ s_{y}\sin\psi & s_{y}\cos\psi & y_{c}\end{array}\right]$ in the programming language you are using. There are left hand and right hand rotation conventions as well as pre or post multiplication operations. Without more details (give out matrix values) after each operation there is no way to correctly answer this question. $\endgroup$ May 7, 2014 at 13:42
5
$\begingroup$

The term for this is matrix decomposition. Here is a solution that includes skew as described by Frédéric Wang.

It operates on a 2d matrix defined as such:

$$\left[\begin{array}{ccc} \mathrm{a} & \mathrm{c} & \mathrm{tx}\\ \mathrm{b} & \mathrm{d} & \mathrm{ty}\end{array}\right]$$

function decompose_2d_matrix(mat) {
  var a = mat[0];
  var b = mat[1];
  var c = mat[2];
  var d = mat[3];
  var e = mat[4];
  var f = mat[5];

  var delta = a * d - b * c;

  let result = {
    translation: [e, f],
    rotation: 0,
    scale: [0, 0],
    skew: [0, 0],
  };

  // Apply the QR-like decomposition.
  if (a != 0 || b != 0) {
    var r = Math.sqrt(a * a + b * b);
    result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
    result.scale = [r, delta / r];
    result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
  } else if (c != 0 || d != 0) {
    var s = Math.sqrt(c * c + d * d);
    result.rotation =
      Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
    result.scale = [delta / s, s];
    result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
  } else {
    // a = b = c = d = 0
  }

  return result;
}
$\endgroup$
1
  • 1
    $\begingroup$ Hi, I've an issue with your code when sx is -1: here is the input matrix: matrix(-1, 0, 0, 1, 0, 0) and as output I get scale = 1, -1 and rotation = - Math.PI. I understand that it's basically equivalent to what I expect (scale -1, 1 and rotation 0) but is there a way to fix it? $\endgroup$
    – lviggiani
    Nov 12, 2021 at 14:39
4
$\begingroup$

Scale and Rotation Extraction for Action Script 3


package nid.utils 
{
    import flash.geom.Matrix;
    import flash.geom.Point;
    import nid.geom.DMatrix;
    /**
     * ...
     * @author Nidin P Vinayakan
     */
    public class MatrixConvertor 
    {
        public static const degree:Number = 180 / Math.PI;
        public static const radian:Number = Math.PI / 180;

        public function MatrixConvertor()
        {

        }
        public static function convert(mat:Matrix):DMatrix 
        {
            var dmat:DMatrix = new DMatrix(mat.a, mat.b, mat.c, mat.d, mat.tx, mat.ty);
            var rad:Number;
            var deg:Number;
            var sign:Number;
            /**
             * scaleX = √(a^2+c^2)
             * scaleY = √(b^2+d^2)
             * rotation = tan^-1(c/d) = tan^-1(-b/a) it will not work sometimes 
             * rotation = a / scaleX  = d / scaleY
             */
            with (dmat)
            {
                scaleX = Math.sqrt((a * a) + (c * c));
                scaleY = Math.sqrt((b * b) + (d * d));

                sign = Math.atan(-c / a);
                rad  = Math.acos(a / scaleX);
                deg  = rad * degree;

                if (deg > 90 && sign > 0)
                {
                    rotation = (360 - deg) * radian;
                }
                else if (deg < 90 && sign < 0)
                {
                    rotation = (360 - deg) * radian;
                }
                else
                {
                    rotation = rad;
                }
                rotationInDegree = rotation * degree;
            }
            return dmat;
        }
    }

}

/**
* DMatrix Class
*/
package nid.geom 
{
    import flash.geom.Matrix;
    /**
     * ...
     * @author Nidin P Vinayakan
     */
    public class DMatrix extends Matrix
    {
        public var rotation:Number=0;
        public var rotationInDegree:Number=0;
        public var scaleX:Number=1;
        public var scaleY:Number=1;

        public function DMatrix(a:Number=1, b:Number=0, c:Number=0, d:Number=1, tx:Number=0, ty:Number=0)
        {
            super(a, b, c, d, tx, ty);
        }

    }

}
$\endgroup$
4
  • 1
    $\begingroup$ Scale and Rotation Extraction for Action Script 3 Thank you for your answer,but i can't understand your answer. for example: what's meaning of the a c degree, radian and so on.... thank you! $\endgroup$
    – user19914
    Nov 22, 2011 at 7:33
  • 1
    $\begingroup$ too bad this answer is incomplete. $\endgroup$ Aug 21, 2013 at 15:41
  • $\begingroup$ i calculated the rotation using float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI)); need to multiply with -1 to get the correcct value . why is tha happen? $\endgroup$
    – DKV
    Nov 15, 2016 at 12:28
  • 1
    $\begingroup$ Hats off to you sir, You saved my week. $\endgroup$ Nov 17, 2016 at 16:50

You must log in to answer this question.