The mathematical principles of Matrix
Translational transform
Rotation transformation
Scaling transformation
Shear transformation
Symmetry transform
Code validation
The mathematical principles of Matrix
In Android, if you’ve ever done image manipulation with Matrix, you probably know the Matrix class. Android Matrix is a 3 x 3 Matrix, and its contents are as follows:
Image processing of Matrix can be divided into four basic transformations:
Translate translation
Rotate Rotate
Scale transformation
Skew tangent transformation
Literally, MSCALE in a matrix is used for scaling, MSKEW for splicing, MTRANS for translation, and MPERSP for perspective. In practice, of course, Matrix cannot be understood literally. At the same time, in the Android documentation, there is no related description of Matrix perspective transformation, so this paper will not discuss this aspect.
Android provides pre, Set, and POST operations for each transformation. Among them
Set is used to set values in the Matrix.
Pre is multiplication first, because matrix multiplication does not satisfy the commutative law, so multiplication first and multiplication after must be strictly distinguished. Multiplying first is equivalent to multiplying right in matrix operations.
Post is the product after, because matrix multiplication does not satisfy the commutative law, so the product before and after must be strictly distinguished. Backward multiplication is equivalent to left multiplication in matrix operations.
In addition to Translate, the Rotate, Scale, and Skew transformations can all be performed around a central point. If not specified, the corresponding transformations are performed around (0, 0) by default.
Now let’s look at the specific cases of the four transformations. Since all graphs are composed of points, we only need to examine a point-dependent transformation.
Let’s say I have a point whose coordinates are theta, move it to, and suppose thatxAxis andyThe magnitude of axial movement is as follows:
As shown below:
It’s not hard to know:
If expressed as a matrix, it can be written as:
Second,Rotation transformation
2.1 Rotation around the origin of coordinates:
Let’s say I have a point, the origin of relative coordinates rotates clockwiseAfter the case, also assumePThe distance between the point and the origin is zeror, as shown below:
So,
If a matrix is used, it can be expressed as:
2.2 Rotation around a point
If I go around some pointClockwise rotation, can be expressed by the matrix as:
Can be reduced to:
Obviously,
1.
It’s moving the origin to the pointLater,The new coordinates of.
2.
It’s the transformation of the last stepRotates clockwise around the origin of the new coordinates 。
3.
After the rotation transformation in the previous step, the coordinate origin is moved back to the original coordinate origin.
Therefore, the rotation transformation around a point can be divided into three steps, that is, first move the coordinate origin to that point, then rotate the transformation around the new coordinate origin, and then move the coordinate origin back to the original coordinate origin.
Three,Scaling transformation
Theoretically, there’s no scaling for a point, but given that all images are made up of points, so if the image is atxAxis andyZoom in on each axisk1andk2Times theta, then theta of all the points in the graphxCoordinates andyThe coordinates will be amplified separatelyk1andk2Times, that is,
In matrix form:
Scaling is a little bit easier to understand, but I won’t go into that.
Four,Shear transformation
Skew, also known as Shear mapping or Transvection in mathematics, is a special linear transformation. The effect of the tangent transformation is to make all the pointsxCoordinates (oryCoordinates) remain constant while corresponding toyCoordinates (orxCoordinates) are translated proportionally, and the size of the translation and the point toxThe vertical distance of the axis (or y axis) is proportional. Tangent transformation belongs to equal area transformation, that is, the area of a shape is the same before and after the tangent transformation.
Like the picture belowyThe coordinates stay the same, but it’sxThe coordinates have been shifted proportionally. This would be a horizontal miscut.
For each point in the picture belowxThe coordinates stay the same, but it’syThe coordinates have been shifted proportionally. This situation is called vertical miscut.
Let’s say I have a pointIt’s going to be given by the tangent transformation, for horizontal miscut, there should be the following relationship:
In matrix form:
The matrix that extends to 3 x 3 looks like this:
Similarly, for vertical mistangent, we can have:
The mathematically rigorous tangent transformation looks something like this. In Android, in addition to the above mentioned situation, you can also carry out horizontal and vertical fault cutting at the same time, so the form is:
Five,Symmetry transform
In addition to the basic transformations described above in 4, we can actually use the Matrix to perform symmetric transformations. The so-called symmetric transformation is that the changed image and the original image are symmetric about a certain axis of symmetry. For example, some pointIt’s going to be a symmetric transformation.
If the axis of symmetry is the x axis,
In matrix form:
If the axis of symmetry is the Y-axis,
In matrix form:
If the axis of symmetry is PIy = xAs shown in figure:
So,
It can be easily solved as:
In matrix form:
Same thing if the axis of symmetry is PIy = -x, then it can be expressed by matrix:
In particular, if the axis of symmetry isy = kx, as shown below:
So,
It can be easily solved:
In matrix form:
whenk = 0When, that is,y = 0, which is the axis of symmetryxShaft condition; whenkAs it approaches infinity, that isx = 0, which is the axis of symmetryyShaft condition; whenk =1, namelyy = x, which is the axis of symmetryy = xThe situation; whenk = –1, namelyy = -x, which is the axis of symmetryy = -xIn the case. It is not difficult to verify that this is consistent with the specific situation mentioned in 4.
If the axis of symmetry is PIy = kx + bIn this case, we only need to add two translation transformations on the basis of the above, that is, first move the coordinate origin to (0, b) and then do the above abouty = kxAnd then move the coordinate origin back to the original coordinate origin. The matrix looks something like this:
Special note: in actual programming, we know about screensyCoordinate forward sum in mathematicsyThe coordinates are just opposite, so mathematicallyy = xAnd on the screeny = -xIs really the same thing, and vice versa. That is, if you want to make the picture on the screen look like a mathematical sensey = xSymmetric, then use this conversion:
Make the picture on the screen look like a mathematical sensey = -xSymmetric, then use this conversion:
The same is true for symmetry y = kx or y = kx + b.
The second partCode validation
The verification codes for various image transformations described in the first part are as follows, and a total of 10 cases are listed. If you want to validate one of these cases, simply uncomment the corresponding code. Images used in the experiment:
Its dimensions are 162 x 251.
The result of each transformation is described below.
view plain
copy
- <span style=“font-size:13px;”></span><pre name=“code” class= “java”>package com.pat.testtransformmatrix;
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Matrix;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.Window;
- import android.view.WindowManager;
- import android.view.View.OnTouchListener;
- import android.widget.ImageView;
- public class TestTransformMatrixActivity extends Activity
- implements
- OnTouchListener
- {
- private TransformMatrixView view;
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- view = new TransformMatrixView(this);
- view.setScaleType(ImageView.ScaleType.MATRIX);
- view.setOnTouchListener(this);
- setContentView(view);
- }
- class TransformMatrixView extends ImageView
- {
- private Bitmap bitmap;
- private Matrix matrix;
- public TransformMatrixView(Context context)
- {
- super(context);
- bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
- matrix = new Matrix();
- }
- @Override
- protected void onDraw(Canvas canvas)
- {
- // Draw the original image
- canvas.drawBitmap(bitmap, 0.0. null);
- // Draw the transformed image
- canvas.drawBitmap(bitmap, matrix, null);
- super.onDraw(canvas);
- }
- @Override
- public void setImageMatrix(Matrix matrix)
- {
- this.matrix.set(matrix);
- super.setImageMatrix(matrix);
- }
- public Bitmap getImageBitmap()
- {
- return bitmap;
- }
- }
- public boolean onTouch(View v, MotionEvent e)
- {
- if(e.getAction() == MotionEvent.ACTION_UP)
- {
- Matrix matrix = new Matrix();
- // Output image width and height (162 x 251)
- Log.e(“TestTransformMatrixActivity”.“image size: width x height = “ + view.getImageBitmap().getWidth() + ” x “ + view.getImageBitmap().getHeight());
- / / 1. Translation
- matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
- GetImageBitmap ().getwidth (), view.getimageBitmap ().getheight ()
- view.setImageMatrix(matrix);
- // The following code is used to view elements in matrix
- float[] matrixValues = new float[9];
- matrix.getValues(matrixValues);
- for(int i = 0; i < 3; ++i)
- {
- String temp = new String();
- for(int j = 0; j < 3; ++j)
- {
- temp += matrixValues[3 * i + j ] + “\t”;
- }
- Log.e(“TestTransformMatrixActivity”, temp);
- }
- // // 2. Rotation (around the center of the image)
- // matrix.setRotate(45f, view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- PostTranslate (view.getimagebitmap ().getwidth () * 1.5f, 0f); // matrix.posttranslate (view.getimagebitmap ().getwidth () * 1.5f, 0f);
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 3. Rotation (around the origin of coordinates) + translation (effect the same as 2)
- // matrix.setRotate(45f);
- // matrix.preTranslate(-1f * view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight() / 2f);
- // matrix.postTranslate((float)view.getImageBitmap().getWidth() / 2f, (float)view.getImageBitmap().getHeight() / 2f);
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- PostTranslate ((float) view.getimagebitmap ().getwidth () * 1.5f, 0f); // matrix.posttranslate ((float) view.getimagebitmap ().getwidth () * 1.5f, 0f);
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 4. Zoom
- // matrix.setScale(2f, 2f);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 5. Wrong cut – level
- / / matrix. SetSkew (0.5 f, 0 f);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(view.getImageBitmap().getWidth(), 0f);
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 6. Wrong cut – vertical
- / / matrix. SetSkew (0 f, 0.5 f);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // 7. Wrong cut – horizontal + vertical
- / / matrix. SetSkew (0.5 0.5 f, f);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 8. Symmetry (horizontal symmetry)
- // float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight() * 2f);
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 9. Symmetrical – vertical
- // float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(view.getImageBitmap().getWidth() * 2f, 0f);
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- // // 10. Symmetry (axis of symmetry is straight line y = x)
- // float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // The code below is to view the elements in the matrix
- // float[] matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- //
- // // do the following translation transformation, purely to make the transformed image and the original image do not overlap
- // matrix.postTranslate(view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth(),
- // view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth());
- // view.setImageMatrix(matrix);
- //
- // // The code below is to view the elements in the matrix
- // matrixValues = new float[9];
- // matrix.getValues(matrixValues);
- // for(int i = 0; i < 3; ++i)
- / / {
- // String temp = new String();
- // for(int j = 0; j < 3; ++j)
- / / {
- // temp += matrixValues[3 * i + j ] + “\t”;
- // }
- // Log.e(“TestTransformMatrixActivity”, temp);
- // }
- view.invalidate();
- }
- return true;
- }
- }
The specific results of various transformations in the above code and their corresponding transformation matrices are given below
1. The translation
Output results:
Please check the correctness of the above matrix against the case described in part 1: translation transformation.
2. Rotate (around the center of the image)
Output results:
It’s actually
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
Matrix. PostTranslate (view. GetImageBitmap () getWidth () * 1.5 f, 0 f);
The result of the combination of these two statements. According to the formula for rotation around a point in Part 1,
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
The resulting transformation matrix is:
And the matrix. PostTranslate (the getImageBitmap (.) getWidth () * 1.5 f, 0 f); To the left of the above matrix multiply by the following matrix:
The fact that post is left multiplied was mentioned earlier in the theory section, and we’ll talk more about it later.
So it’s actually:
When we go out to calculate the accuracy error, we can see that the result we calculated is consistent with the result directly output by the program.
3. Rotation (rotation around the origin of coordinates, plus two translations, the effect is the same as 2)
According to the explanation about rotation around a point in part 1, “Ii. Rotation Transformation”, it is not difficult to know:
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
Is equivalent to
matrix.setRotate(45f);
matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f);
matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);
The corresponding matrix of matrix. SetRotate (45f) is:
Matrix. PreTranslate (-1f* view.getimagebitmap ().getwidth ()/ 2f, -1f* view.getimagebitmap ().getheight ()/ 2f)
Since it is preTranslate, it is multiply first, that is, multiply right, that is, it should appear on the right side of the matrix corresponding to matrix.setrotate (45f).
PostTranslate ((float) view.getimageBitmap ().getwidth ()/ 2f, (float) view.getimagebitmap ().getheight ()/ 2f)
This time, since it’s postTranslate, it’s a back multiplier, that is, a left multiplier, which means it should appear on the left side of the matrix corresponding to matrix.setrotate (45f).
So put it all together,
matrix.setRotate(45f);
matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f);
matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);
The corresponding matrix is:
This is the same as the following matrix (rotated 45 degrees clockwise around the center of the image) :
Therefore, the transformed image here is the same as the transformed image in 2.
4. Scale transformation
The two matrices output by the program are:
The second matrix is actually the product of two matrices:
You can compare the first part of the “three, scaling transformation” and “one, translation transformation” statement, verify the results.
5. Miscut transformation (horizontal miscut)
The two matrices output from this code are:
Where the second matrix is actually the product of the following two matrices:
You can compare the first part of the “four, wrong tangent transformation” and “one, translation transformation” related statements, verify the results.
6. Miscut transformation (vertical miscut)
The two matrices output from this code are:
Where the second matrix is actually the product of the following two matrices:
You can compare the first part of the “four, wrong tangent transformation” and “one, translation transformation” related statements, verify the results.
7. Miscut transformation (horizontal + vertical miscut)
The two matrices output from this code are:
Where, the latter is the product of the following two matrices:
You can compare the first part of the “four, wrong tangent transformation” and “one, translation transformation” related statements, verify the results.
8. Symmetric transformation (horizontal symmetry)
The two matrices output from the code are:
Where, the latter is the product of the following two matrices:
You can compare the first part of the “five, symmetric transformation” and “one, translation transformation” related statements, verify the results.
9. Symmetric transformation (vertical symmetry)
The two matrices output from this code are:
Where, the latter is the product of the following two matrices:
You can compare the first part of the “five, symmetric transformation” and “one, translation transformation” related statements, verify the results.
10. Symmetry transformation (the axis of symmetry is a straight liney = x)
The two matrices output from this code are:
Where, the latter is the product of the following two matrices:
You can compare the first part of the “five, symmetric transformation” and “one, translation transformation” related statements, verify the results.
11. The question of first and last multiplying
Because the matrix multiplication operation does not satisfy the commutative law, we have mentioned the problem of first multiplication and then multiplication many times in front, that is, first multiplication is the right multiplication in matrix operation, and then multiplication is the left multiplication in matrix operation. In fact, the concept of multiply first and multiply later is for the time sequence of transformation operations, while left and right multiply are for the left and right positions of matrix operations. Take the case of rotation around a point in the first part “Ii. Rotation Transformation” as an example:
The closer it is to the matrix of pixels in the original image, the closer it is to the matrix of pixels in the original image, the more backward it is multiplied. In fact, in the image processing, the matrix operation goes from the right to the left. This forms a matrix that is more to the right (multiply by right), more first (multiply by first), and vice versa.
Of course, in practice, if we specify a matrix first, such as setRotate(), that is, specify the middle matrix in the above transformation matrix, so whether the subsequent matrix is pre or post operation depends on the middle matrix.