从仿射变换矩阵中获取倾斜或旋转值

4
我正在尝试从Flash电影片段中获取转换矩阵的偏移值。该转换矩阵由以下代码表示:
a b tx
c d ty
0 0 1

我不知道进行了什么类型的变换以及哪个先执行。我知道在flash中,你只能旋转OR倾斜电影剪辑(如果我错了,请纠正我)。我可以从电影剪辑的scaleX和scaleY属性中获取比例值。我相信平移并不重要,我只需要将tx和ty等于零。

所以我的问题有两个部分。如何确定是否应用了倾斜或旋转,以及如何获取相应的值?


你在这方面的进展如何?我曾经遇到过同样的麻烦,并且有一个可用于有限使用的可行版本(禁止倾斜,并将每个位图转换为符号)。虽然有些不太规范,但是它确实有效。如果你仍然感兴趣,请联系我。 - yihuang
又一个孤独的流浪者...我无法理解这一点。如果有一个像我们这样的俱乐部,请考虑我。我正在编写一个Flump运行时。 - sixFingers
4个回答

2
2D旋转矩阵为:
cos(theta) -sin(theta)
sin(theta)  cos(theta)

如果您没有应用缩放或剪切,

   a = d
and
   c = -b
and the angle of rotation is
   theta = asin(c) = acos(a)

如果已经使用了缩放并且可以恢复缩放因子sx和sy,只需在原始变换矩阵中将第一行除以sx,第二行除以sy,然后像上面一样恢复旋转角度。
如果有剪切(倾斜)应用于其中任何一个地方,我赞同之前评论者的观点,除非在非常有限的情况下(例如仅在已知方向上以已知顺序进行剪切),否则可能不可能。

2

这个术语叫做矩阵分解。以下是一个包括 Frédéric Wang 描述的斜切解决方案。

当变换按照这个顺序应用时有效:斜切、缩放、旋转、平移。

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

2
你需要进行极分解。这篇维基百科文章讲解了它的工作原理:http://en.wikipedia.org/wiki/Polar_decomposition 这是我使用OpenCV库为自己的程序编写的代码。

const double PI = 3.141592653;
    cv::Mat rotationOutput = cv::Mat::zeros(warp00.size(),CV_64F);
    cv::Mat_<double>::iterator rotIter = rotationOutput.begin<double>();
    cv::Mat_<double>::iterator warp00Iter = warp00.begin<double>();
    cv::Mat_<double>::iterator warp01Iter = warp01.begin<double>();
    cv::Mat_<double>::iterator warp10Iter = warp10.begin<double>();
    cv::Mat_<double>::iterator warp11Iter = warp11.begin<double>();

    for(; warp00Iter != warp00.end<double>(); ++warp00Iter, ++warp01Iter, ++warp10Iter,
        ++warp11Iter, ++rotIter){
        cv::Matx22d fMatrix(*warp00Iter,*warp01Iter, *warp10Iter, *warp11Iter);
        cv::Matx22d cMatrix;
        cv::Matx22d cMatSqrt(0.,0.,0.,0.);
        cv::mulTransposed(fMatrix, cMatrix, true);
        cv::Matx21d eigenVals;
        cv::Matx22d eigenVecs;
        if((cMatrix(0,0) !=0.) && (cMatrix(1,1) !=0.)){
            if(cv::eigen(cMatrix,true,eigenVals,eigenVecs)){
                cMatSqrt = eigenVecs.t()*
                        cv::Matx22d(sqrt(eigenVals(0,0)),0.,0.,sqrt(eigenVals(1,0)))*eigenVecs;
            }
        }
        cv::Matx22d rMat = fMatrix*cMatSqrt.inv();
        *rotIter = atan(rMat(1,0)/rMat(0,0));
        
    }

warp00、warp01、warp10和warp11包含仿射变换的前4个参数(平移参数warp02和warp12不需要)。在您的情况下,它将是a、b、c、d。 在维基百科文章中,您会注意到需要计算矩阵的平方根。唯一的方法是计算特征值,然后计算它们的平方根,并将对角矩阵旋转回原来的坐标系。 虽然有些复杂,但这是计算仿射变换旋转的唯一方法。 在我的情况下,我只关心旋转,所以我的代码不会给你提供倾斜。


0

首先,您可以同时进行倾斜和旋转,但必须先选择顺序。一个倾斜矩阵在这里有解释,要将倾斜矩阵添加到变换中,您需要创建一个新矩阵并执行yourTransformMatrix.concat(skewMatrix);

目前我无法确定是否可以以“旋转角度”、“倾斜_X角度”、“倾斜_Y角度”、“平移_X”、“平移_Y”的形式检索变换的值,这通常是一个非线性方程系统,可能没有特定矩阵的解。


我并不是在尝试制作变换矩阵。我正在导出设计师制作的Flash动画,这是通过UI完成的,所以我无法确定哪些变换首先进行。 - tzl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接