28

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?

NVI
  • 103

3 Answers3

49

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)$

John Alexiou
  • 13,816
  • 1
    thank you but sx and sy will always be positive because of power 2. what about negative scale values? –  Dec 05 '10 at 22:20
  • 1
    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. – John Alexiou Dec 05 '10 at 22:29
  • ja72, could you complete your answer and find the rotation (ψ)? – NVI May 06 '14 at 02:53
  • 1
    See edit now... – John Alexiou May 06 '14 at 12:44
  • sx and sy values wrongly calculated as negative for rotate(180), since cos(180°) is -1. – NVI May 07 '14 at 04:55
  • So use $s_{x}=\sqrt{\rm{a}^{2}+\rm{b}^{2}}$ etc... – John Alexiou May 07 '14 at 04:58
  • $\sqrt{\mathrm{a}^{2}+\mathrm{b}^{2}}$ is always positive, I need to handle negative values as well. Also, consider scale(-1, 1) rotate(90). ψ gets calculated as -90° instead of 90°. – NVI May 07 '14 at 05:18
  • 1
    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. – John Alexiou May 07 '14 at 12:49
  • 2
    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. – John Alexiou May 07 '14 at 13:42
  • 1
    Related question and answer: http://stackoverflow.com/a/4361442/380384 – John Alexiou May 21 '14 at 20:10
  • calculated the rotation using the formula float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI)); need to multiply with a _1 to get the correct rotaion, why is tha happing? – DKV Nov 15 '16 at 12:17
  • BTW, Is there any method to extract the center of rotation with RigidTransform Matrix ? – Changju.rhee Jun 30 '20 at 21:03
  • @Changju.rhee - Sounds like a question you can ask on [SO]. Make sure you reference the structure of RigidTransform in your question. – John Alexiou Jun 30 '20 at 23:14
  • @JohnAlexiou I have 2D data sets such as source and destination. I used openCV library to get RigidTransform matrix such as estimateRigidtransform function. I'm looking for the center of rotation from this matrix. Is it possible or not ? – Changju.rhee Jul 01 '20 at 00:07
  • The comments isn't the place to ask questions. Post a new question and see who can answer it. I haven't worked with OpenCV to tell you if it is possible or not. – John Alexiou Jul 01 '20 at 00:17
5

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;
}
  • 1
    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? – lviggiani Nov 12 '21 at 14:39
4

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);
        }

    }

}
  • 1
    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! –  Nov 22 '11 at 07:33
  • 1
    too bad this answer is incomplete. – chamberlainpi Aug 21 '13 at 15:41
  • 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? – DKV Nov 15 '16 at 12:28
  • 1
    Hats off to you sir, You saved my week. – Rahul Prasad Nov 17 '16 at 16:50