AndroidNote icon indicating copy to clipboard operation
AndroidNote copied to clipboard

关于矩阵前乘和后乘的问题

Open panpeng0014 opened this issue 8 years ago • 18 comments

Matrix m = new Matrix(); m.reset(); m.preTranslate(tx, ty); //使用pre,越靠后越先执行。 m.preScale(sx, sy);

这里为什么越靠后先执行啊,矩阵乘法我记得是从左往右进行乘法计算的呀(╯‵□′)╯︵┻━┻

panpeng0014 avatar Aug 31 '16 08:08 panpeng0014

这个在Matrix原理一文中有解释。

Pre与Post的区别

主要区别其实就是矩阵的乘法顺序不同,pre相当于矩阵的右乘,而post相当于矩阵的左乘。

以下观点存在歧义,故做删除标注:

在图像处理中,越靠近右边的矩阵越先执行,所以pre操作会先执行,而post操作会后执行。

在实际操作中,我们每一步操作都会得出准确的计算结果,但是为什么还会用存在先后的说法? 难道真的能够用pre和post影响计算顺序? 实则不然,下面我们用一个例子说明:

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.8f);
matrix.preTranslate(1000, 1000);
Log.e(TAG, "MatrixTest:3" + matrix.toShortString());

在上面的操作中,如果按照正常的思路,先缩放,后平移,缩放操作执行在前,不会影响到后续的平移操作,但是执行结果却发现平移距离变成了(500, 800)。

在上面例子中,计算顺序是没有问题的,先计算的缩放,然后计算的平移,而缩放影响到平移则是因为前一步缩放后的结果矩阵右乘了平移矩阵,这是符合矩阵乘法的运算规律的,也就是说缩放操作虽然在前却影响到了平移操作,相当于先执行了平移操作,然后执行的缩放操作,因此才有pre操作会先执行,而post操作会后执行这一说法

GcsSloop avatar Aug 31 '16 08:08 GcsSloop

这一段的解释并不明白,有两个变换矩阵和初始矩阵相乘的话,必然会导致两个矩阵相互影响的,这不能说明为什么会先执行平移操作,然后执行缩放操作

panpeng0014 avatar Aug 31 '16 10:08 panpeng0014

矩阵计算,每一步都会计算出确定的结果,执行顺序就是按照你程序的顺序,只不过有的是左乘,有的是右乘,用矩阵乘法规则推算一下就明白了。

GcsSloop avatar Aug 31 '16 11:08 GcsSloop

计算顺序是没有问题的,先计算的缩放,然后计算的平移,而缩放影响到平移则是因为前一步缩放后的结果矩阵右乘了平移矩阵

GcsSloop avatar Aug 31 '16 11:08 GcsSloop

我计算了一下,确实最后的点的结果是 相当于先进行了平移操作在进行了缩放操作。。

panpeng0014 avatar Sep 01 '16 03:09 panpeng0014

对应代码:
 matrix.postScale(0.5f, 0.5f);  
 matrix.preTranslate(-pivotX, -pivotY);  
 matrix.postTranslate(pivotX, pivotY);  

大神,这段代码的矩阵的排序是 PostTranslateMatrix * PostScaleMatrix * InitialMatrix * PreTranslateMatrix 吗

CSnowStack avatar Oct 18 '16 03:10 CSnowStack

理论上是这样的,但是要注意计算顺序,计算顺序依旧是按照代码书写顺序进行计算的。

GcsSloop avatar Oct 18 '16 07:10 GcsSloop

什么意思,按我那样写来计算的值好像是正确的,按代码书写那样来计算,结果好像是 0.5 , 0 , 0 0 , 0.5 , 0 0 , 0 , 1

CSnowStack avatar Oct 18 '16 08:10 CSnowStack

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Log.i("Matrix", matrix.toShortString());
matrix.preTranslate(-pivotX, -pivotY);
Log.i("Matrix", matrix.toShortString());
matrix.postTranslate(pivotX, pivotY);
Log.i("Matrix", matrix.toShortString());

上面这段代码的含义是按照某个点(pivotX, pivotY)的位置进行缩放。 执行结果如下:

Matrix: [0.5, 0.0, 0.0][0.0, 0.5, 0.0][0.0, 0.0, 1.0]
Matrix: [0.5, 0.0, -8421594.0][0.0, 0.5, -8421595.0][0.0, 0.0, 1.0]
Matrix: [0.5, 0.0, 8421594.0][0.0, 0.5, 8421595.0][0.0, 0.0, 1.0]

其中每一步都得到了确定的结果,最终的结果是上面这样的。

你可以按照 PostTranslateMatrix * PostScaleMatrix * InitialMatrix * PreTranslateMatrix 这样的顺序来理解,由于矩阵乘法满足结合律,按照这样顺序计算也没错,但是这并非实际计算顺序,仅此而已。

GcsSloop avatar Oct 18 '16 08:10 GcsSloop

明白了,我sb了,原来前乘后乘是要一步步看的,谢谢大神!!!

CSnowStack avatar Oct 18 '16 09:10 CSnowStack

楼主,你们在讨论矩阵的结合律吗?

MarsToken avatar Nov 15 '16 09:11 MarsToken

差不多 ╮ ( ̄ 3 ̄) ╭

GcsSloop avatar Nov 15 '16 09:11 GcsSloop

/** * Preconcats the matrix with the specified translation. * M' = M * T(dx, dy) */ public boolean preTranslate(float dx, float dy) { native_preTranslate(native_instance, dx, dy); return true; } 注释很清楚, 用指定的矩阵T(dx,dy)前乘矩阵M, 即M'= M * T, 故而pre表示M后乘变换矩阵T.

showdy avatar Dec 13 '17 07:12 showdy

你好,我想请问下 canvas中的contact()方法是前乘(pre) 还是后乘(post) 操作, 又比如之前对matrix进行了一堆变换, 这时候如果我想基于当前的变换 再旋转30度, 那这时候改使用 preRotate 还是postRotate,因为矩阵不满足交换律,这里两种情况一定是不一样的 , 那这样 改怎么理解和使用 pre和post

zhouhong0607 avatar Aug 14 '18 03:08 zhouhong0607

http://www.gcssloop.com/customview/Matrix_Basic

GcsSloop avatar Aug 14 '18 05:08 GcsSloop

你好我是看过了这篇文章后才有的这两个疑问,文章看了但是还是没有搞明白,主要是两个内容 1.我理解 canvas中的contact就是 一个乘法操作,毕竟Matrix中也有 pre和post的contact方法,那么canvas中的contact操作相当于 pre还是post? 2.第二个没理解的就是pre和post到底该怎么使用,基于某一点旋转这个可以理解,但是如果一个矩阵经过了一系列组合变化(旋转、位移、缩放)后得到一个矩阵, 那么如果我想在视觉在对变换后的图像再进行操作,比如再tranlate 1000个像素,或者再旋转 30度, 那这时候该使用pre还是使用post。

感谢您的时间。

zhouhong0607 avatar Aug 14 '18 05:08 zhouhong0607

  1. pre
  2. 以最终效果为准. 实战中理解不清楚的话就两种都试一下,看哪个是自己最终需要的视觉效果.

GcsSloop avatar Aug 14 '18 06:08 GcsSloop

3q, 刚刚测试 pre是受之前matrix变换的影响, post不受之前matrix变换的影响, 不知道这个结论对吗?

zhouhong0607 avatar Aug 14 '18 12:08 zhouhong0607