Android Matrix Source Detailed _android

Source: Internet
Author: User

The mathematical principle of matrix

In Android, if you use the matrix for image processing, you must know the Matrix class. The Matrix in Android is a 3 x 3, which reads:

The matrix image processing can be divided into four kinds of basic transformations:

    • Translate Translation Transformation
    • Rotate Rotation Transform
    • Scale Scaling Transform
    • Skew Error-Cut transform

Literally, the Mscale in the matrix is used to handle the scaling transformation, Mskew is used to handle the tangent transformation, Mtrans is used to handle the translation transformation, and the MPERSP is used to handle perspective transformations. In practice, of course, the matrix is not fully understood in terms of the literal version. At the same time, in the Android document, the matrix is not seen for the perspective of the relevant instructions, so this article does not discuss this issue.

For each transformation, Android provides three ways to operate the pre, set, and post. which

Set sets the values in the matrix.

Pre is the first multiplication, because the multiplication of matrices does not satisfy the Exchange law, so the first multiplication, the multiplier must be strictly differentiated. The first multiplication corresponds to the right multiplication in the matrix operation.

Post is a multiplier because the multiplication of matrices does not satisfy the commutative law, so the first multiplication and the back multiplication must be strictly differentiated. The multiplier corresponds to the left multiplication in the matrix operation.

In addition to translational Transformations (Translate), rotational Transformations (Rotate), scaling Transformations (Scale), and staggered transformations (Skew) can be performed around a central point, and if not specified, the corresponding transformations are made around (0, 0) by default.

Now let's take a look at the specifics of the four transformations. Since all graphics are a bit of a component, we just need to look at a point-dependent transformation.

A translation transformation

Assume that the coordinates of a point are, move it to, and then assume that the size of the move in the X and Y axes is:

As shown in the following illustration:

It is not difficult to know:

If you use a matrix to express, you can write:

Two, rotation transformation

2.1 Rotate around the origin of the coordinates:

Suppose there is a point where the relative coordinate origin rotates clockwise, assuming the P-point distance from the origin of the coordinate is r, as shown below:

So

If you use a matrix, it can be expressed as:

2.2 Rotate around a point

If it's around a point

Can be translated into:

It is clear that

1. Is the new coordinates that move the coordinate origin to the point.

2. Is the transformation of the previous step, around the new coordinates of the origin clockwise rotation.

3.

After the previous rotation transformation, the coordinate origin is then moved back to the original coordinate Origin point.

So, the rotational transformation around a point can be divided into 3 steps, that is, first move the coordinate origin to that point, then rotate the transformation around the new coordinate origin, then move the coordinate origin back to the original coordinate Origin point.

third, scaling transformation

Theoretically, there is no scaling transformation for one point, however, considering that all images are made up of dots, the x and Y coordinates of all points in the image are magnified K1 and K2 times, respectively, if the image is magnified K1 and K2 times respectively in the X and Y axes, i.e.

Represented by a matrix is:

Zoom transformation is better to understand, do not say more.

Four, the wrong cut transformation

The skew is mathematically called the shear mapping (which can be translated as a "shear transformation") or transvection (contraction), which is a special linear transformation. The effect of the tangent transformation is to keep the x-coordinate (or y-coordinate) of all points unchanged, while the corresponding y-coordinate (or x-coordinate) translates proportionally, and the translation is proportional to the vertical distance from the point to the x-axis (or y-axis). The wrong-cut transformation belongs to the equal-area transformation, that is, the area of a shape is equivalent before and after the wrong-cut transformation.

For example, the y-coordinate of each point remains unchanged, but its x-coordinate translates proportionally. This situation will be the level of the wrong cut.

The x-coordinate of each point in the following figure remains unchanged, but its y-coordinate translates proportionally. This condition is called vertical wrong cut.

Assuming that a point is obtained by a staggered transformation, for horizontal mismatches, the following relationships should be:

Represented by a matrix is:

The matrix that expands to 3 x 3 is the following form:

Similarly, for vertical dislocations, you can have:

In mathematics, the strict wrong-cut transformation is the case above. In addition to the above mentioned in Android, can also be horizontal, vertical wrong, then the form is:

V. Symmetry transformation

In addition to the basic transformations mentioned in the 4 above, we can actually use the matrix for symmetric transformations. The so-called symmetric transformation, is the changed image and the original image is about an axis of symmetry is symmetric. For example, a point is obtained after a symmetric transformation,

If the axis of symmetry is the x-axis, is it difficult,

Represented by a matrix is:

If the axis of symmetry is the Y axis, then

Represented by a matrix is:

If the axis of symmetry is y = x, as shown in figure:

So

It's easy to get the solution:

Represented by a matrix is:

Similarly, if the axis of symmetry is y =-X, then the matrix representation is:

Particularly if the axis of symmetry is y = kx, the following figure:

So

It's easy to solve:

Represented by a matrix is:

When k = 0 o'clock, that is, y = 0, which is the axis of the x axis, when k tends to infinity, that is, x = 0, which is the case where the axis of symmetry is the y-axis, when k = 1 o'clock, that is, y = x, where the axis of symmetry is y = x, and when k =-1, that is, y =-X, which is the axis of y =-X. It is not difficult to verify that this is consistent with the specific situation we mentioned earlier in 4.

If the axis of symmetry is y = kx + B, only on the basis of the above to add two translation transformation, that is, first move the coordinate origin to (0, B), and then do the above about y = kx symmetric transformation, and then move the coordinate origin back to the original coordinates of the original point can be. The matrix representation is roughly like this:

Special attention needs to be paid to: in practical programming, we know that the y-coordinate of the screen is exactly the opposite of the y-coordinate in mathematics, so y = x and Y on the screen are really the same thing, and vice versa. That is, if you want to make the picture appear on the screen as if it is mathematically y = x symmetric, you need to use this conversion:

To make a picture appear on the screen as if y = x is mathematically symmetric, you need to use this conversion:

As for the case of the axis of symmetry as Y = kx or y = kx + B, it is also necessary to consider the problem.

Part Two code validation

The verification code for the various image transformations mentioned in the first section is as follows, which lists 10 scenarios altogether. If you want to verify one of these cases, simply uncomment the appropriate code. The pictures used in the experiment:

Its size is 162 x 251.

The results of each transformation, see the description after the code.

Package Compattesttransformmatrix; 
Import androidappactivity; 
Import Androidcontentcontext; 
Import Androidgraphicsbitmap; 
Import Androidgraphicsbitmapfactory; 
Import Androidgraphicscanvas; 
Import Androidgraphicsmatrix; 
Import Androidosbundle; 
Import Androidutillog; 
Import androidviewmotionevent; 
Import Androidviewview; 
Import Androidviewwindow; 
Import Androidviewwindowmanager; 
Import Androidviewviewontouchlistener; 
 
Import Androidwidgetimageview; public class Testtransformmatrixactivity extends activity implements Ontouchlistener {private Transformmatrixview VI 
 ew 
  @Override public void OnCreate (Bundle savedinstancestate) {superoncreate (savedinstancestate); 
  Requestwindowfeature (Windowfeature_no_title); 
 
  Thisgetwindow () setflags (Windowmanagerlayoutparamsflag_fullscreen, Windowmanagerlayoutparamsflag_fullscreen); 
  view = new Transformmatrixview (this); 
  Viewsetscaletype (Imageviewscaletypematrix); 
   
  Viewsetontouchlistener (this); SetcontentvIew (view); 
  Class Transformmatrixview extends ImageView {private Bitmap Bitmap; 
  Private matrix matrix; 
   Public Transformmatrixview {Super (context); 
   Bitmap = Bitmapfactorydecoderesource (Getresources (), Rdrawablesophie); 
  Matrix = new Matrix (); 
   @Override protected void OnDraw (Canvas Canvas) {//Draw the original image Canvasdrawbitmap (bitmap, 0, 0, NULL); 
   Draw the transformed image Canvasdrawbitmap (bitmap, matrix, null); 
  Superondraw (canvas); 
   @Override public void Setimagematrix (Matrix matrix) {Thismatrixset (matrix); 
  Supersetimagematrix (matrix); 
  Public Bitmap Getimagebitmap () {return Bitmap; } public boolean Ontouch (View V, motionevent e) {if (egetaction () = = motioneventaction_up) {Matrix ma 
   Trix = new Matrix (); The width and height of the output image (162 x 251) Loge ("testtransformmatrixactivity", "image size:width x Height =" + viewgetimagebitmap () get Width () + "X" + ViewgetimagebiTMap () getheight ()); 
   Translational Matrixposttranslate (Viewgetimagebitmap () getwidth (), Viewgetimagebitmap () getheight ()); 
    
   Translates Viewgetimagebitmap () getwidth () in the X direction, Viewgetimagebitmap () getheight () Viewsetimagematrix () in the y-axis direction (matrix); 
   The following code is to view the elements in the matrix float[] matrixvalues = new FLOAT[9]; 
   Matrixgetvalues (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"; 
   } Loge ("testtransformmatrixactivity", temp); ////rotate (center point around image)//Matrixsetrotate (45f, Viewgetimagebitmap () getwidth ()/2f, Viewgetimagebitmap () Getheig 
HT ()/2f); 
//Do the following translation transformation, purely to allow the transformed image and the original image does not overlap//matrixposttranslate (Viewgetimagebitmap () getwidth () * 5f, 0f); 
Viewsetimagematrix (matrix); 
//The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); for (int i = 0; i < 3; ++i)//{ 
The string temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////rotate (around coordinate origin) + translate (effect same 2)//Matrixsetrotate (45f); 
Matrixpretranslate ( -1f * VIEWGETIMAGEBITMAP () getwidth ()/2f, -1f * VIEWGETIMAGEBITMAP () getheight ()/2f); 
Matrixposttranslate ((float) viewgetimagebitmap () getwidth ()/2f, (float) viewgetimagebitmap () getheight ()/2f); 
//Do the following translation transformation, purely to allow the transformed image and the original image does not overlap//matrixposttranslate ((float) viewgetimagebitmap () getwidth () * 5f, 0f); 
Viewsetimagematrix (matrix); 
//The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); //}////Zoom//Matrixsetscale (2f, 2f); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); //////Do the following translation transformation purely to allow the transformed image and the original image to not overlap//matrixposttranslate (Viewgetimagebitmap () getwidth (), Viewgetimageb 
Itmap () getheight ()); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////Wrong Cut-horizontal//Matrixsetskew (5f, 0f); The following code is to view the MAElements in Trix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//////Do the following translation transformation purely to allow the transformed image and the original image to not overlap//matrixposttranslate (Viewgetimagebitmap () getwidth (), 0f); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////Wrong Cut-vertical//Matrixsetskew (0f, 5f); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); for (int i = 0; I < 3; 
++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//////Do the following translation transformation purely to allow the transformed image and the original image to not overlap//matrixposttranslate (0f, Viewgetimagebitmap () getheight ()); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//Wrong Cut-horizontal + Vertical//Matrixsetskew (5f, 5f); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); for (int j = 0; j < 3; ++j)
{//Temp + + matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//////Do the following translation transformation purely to allow the transformed image and the original image to not overlap//matrixposttranslate (0f, Viewgetimagebitmap () getheight ()); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////symmetry (horizontal symmetry)//float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f}; 
Matrixsetvalues (matrix_values); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//////Do the following translation transformation purely to allow the transformed image and original image to not overlap//matrixposttranslate (0f, Viewgetimagebitmap () getheight () * 2f); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////symmetry-vertical//float matrix_values[] = { -1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f}; 
Matrixsetvalues (matrix_values); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); for (int j = 0; j < 3; ++j)//{//Temp + + matrixvalues[3 * i + J] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
//////Do the following translation transformation purely to allow the transformed image and original image not to overlap//matrixposttranslate (Viewgetimagebitmap () getwidth () * 2f, 0f); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
////symmetry (symmetry axis is straight y = x)//float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f}; 
Matrixsetvalues (matrix_values); 
The following code is to view the elements in the matrix//float[] matrixvalues = new FLOAT[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T";
}//Loge ("testtransformmatrixactivity", temp); //////Do the following translation transformation purely to allow the transformed image and the original image to not overlap//matrixposttranslate (Viewgetimagebitmap () getheight () + Viewgeti 
Magebitmap () getwidth (),//Viewgetimagebitmap () getheight () + Viewgetimagebitmap () getwidth ()); 
Viewsetimagematrix (matrix); 
//The following code is to see the elements in the matrix//matrixvalues = new float[9]; 
Matrixgetvalues (matrixvalues); 
for (int i = 0; i < 3; ++i)//{/\ temp = new string (); 
for (int j = 0; j < 3; ++j)//{//Temp + = Matrixvalues[3 * i + j] + "T"; 
}//Loge ("testtransformmatrixactivity", temp); 
  } viewinvalidate (); 
 return true; 
 } 
}

In the above code, the specific results of various transformations and their corresponding related transformation matrices are given below.

1. Translation

Results of output:

The correctness of the above matrices is investigated in the context of the first and translational transformations described in part one.

2. Rotate (around the center point of the image)

Results of output:

It's actually

Matrix.setrotate (45f,view.getimagebitmap (). GetWidth ()/2f, View.getimagebitmap (). GetHeight ()/2f);

Matrix.posttranslate (View.getimagebitmap (). GetWidth () * 1.5f, 0f);

The results of the combined action of these two statements. According to the formula of rotation around a point in the first part of "Two rotation transformation",

Matrix.setrotate (45f,view.getimagebitmap (). GetWidth ()/2f, View.getimagebitmap (). GetHeight ()/2f);

The resulting transformation matrix is:

and Matrix.posttranslate (View.getimagebitmap (). GetWidth () * 1.5f, 0f) means that the left of the above matrix is multiplied by the following matrix:

As for the post is left multiply, we have mentioned in the previous theoretical section, we will discuss this issue in the following.

So it's actually:

Out of the calculation of the precision error, we can see that we calculate the results, and the direct output of the program is consistent.

3. Rotate (rotate around the origin of the coordinate, add two times to translate, the effect is same as 2)

According to the first part of the "two, rotating transformation" inside the interpretation of rotation around a point, it is not difficult to know:

Matrix.setrotate (45f,view.getimagebitmap (). GetWidth ()/2f, View.getimagebitmap (). GetHeight ()/2f);

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

Where the Matrix.setrotate (45f) corresponds to the matrix is:

Matrix.pretranslate ( -1f* view.getimagebitmap (). GetWidth ()/2f, -1f * VIEW.GETIMAGEBITMAP (). GetHeight ()/2f) The corresponding matrix is:

Because it is pretranslate, it is the first multiplication, that is, the right multiplication, that is, it should appear on the right side of the Matrix.setrotate (45f) corresponding matrix.

Matrix.posttranslate ((float) view.getimagebitmap (). GetWidth ()/2f, (float) view.getimagebitmap (). GetHeight ()/2f) The corresponding matrices are:

This time, because it is posttranslate, is the back, that is, the left, that is, it should appear in the Matrix.setrotate (45f) to the left of the corresponding matrix.

So in combination,

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. Zoom Transform

The two matrices that the program outputs are:

The second matrix, in fact, is the result of multiplying the following two matrices:

We can verify the results by comparing the "three", "Scaling" and "one, translation" in the first part.

5. Wrong-cut transformation (horizontal wrong cut)

The two matrices that the code outputs are:

Where the second matrix is actually the result of multiplying the following two matrices:

You can compare the first part of the "Four, the wrong cut transformation" and "one, translation transformation" of the relevant statements, the results of their own verification.

6. Wrong-cut transformation (vertical wrong cut)

The two matrices that the code outputs are:

Where the second matrix is actually the result of multiplying the following two matrices:

You can compare the first part of the "Four, the wrong cut transformation" and "one, translation transformation" of the relevant statements, the results of their own verification.

7. Wrong-cut transformation (horizontal + vertical wrong cut)

The two matrices that the code outputs are:

Where the latter is the result of multiplying the following two matrices:

You can compare the first part of the "Four, the wrong cut transformation" and "one, translation transformation" of the relevant statements, the results of their own verification.

8. Symmetric transformations (horizontal symmetry)

The two matrices that the code outputs are:

Where the latter is the result of multiplying the following two matrices:

We can compare the "five, symmetric transformations" and "One, translation transformations" in the first part to verify the results.

9. Symmetric transformation (vertical symmetry)

The two matrices that the code outputs are:

Where the latter is the result of multiplying the following two matrices:

We can compare the "five, symmetric transformations" and "One, translation transformations" in the first part to verify the results.

10. Symmetrical transformation (the symmetry axis is a line y = x)

The two matrices that the code outputs are:

Where the latter is the result of multiplying the following two matrices:

We can compare the "five, symmetric transformations" and "One, translation transformations" in the first part to verify the results.

11. On the question of first and rear multiplication

Because the multiplication of the matrix does not satisfy the Exchange law, we have mentioned many times before, that is, the first multiplication is the matrix operation in the right multiplication, the latter is the matrix operation in the left. In fact, the concept of first multiplication and back multiplication is for the time of the transformation operation, the left multiplication and the right multiplication are for the position of the matrix operation. Take the case of rotation around a point in the first part of "Two, rotating transformations":

The closer the pixel in the original image of the Matrix, the more first multiply, farther away from the original image of the pixel matrix, the more multiply. In fact, when the image is processed, the operation of the matrix is done from the right side to the left direction. This forms the more on the right side of the matrix (right), the more first (multiply), and vice versa.

Of course, in practice, if you first specify a matrix, such as we first setrotate (), that is, specify the above transformation matrix, the middle of that matrix, then the next matrix in the end is the pre or post operation, is relative to this intermediate matrix.

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.