Android中图像变换Matrix的原理、代码验证和应用(二)

时间:2022-05-11 21:06:30

第二部分 代码验证

在第一部分中讲到的各种图像变换的验证代码如下,一共列出了10种情况。如果要验证其中的某一种情况,只需将相应的代码反注释即可。试验中用到的图片:

Android中图像变换Matrix的原理、代码验证和应用(二)

其尺寸为162 x 251。

每种变换的结果,请见代码之后的说明。

  1. import android.app.Activity;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Canvas;
  6. import android.graphics.Matrix;
  7. import android.os.Bundle;
  8. import android.util.Log;
  9. import android.view.MotionEvent;
  10. import android.view.View;
  11. import android.view.Window;
  12. import android.view.WindowManager;
  13. import android.view.View.OnTouchListener;
  14. import android.widget.ImageView;
  15. public class TestTransformMatrixActivity extends Activity
  16. implements
  17. OnTouchListener
  18. {
  19. private TransformMatrixView view;
  20. @Override
  21. public void onCreate(Bundle savedInstanceState)
  22. {
  23. super.onCreate(savedInstanceState);
  24. requestWindowFeature(Window.FEATURE_NO_TITLE);
  25. this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
  26. view = new TransformMatrixView(this);
  27. view.setScaleType(ImageView.ScaleType.MATRIX);
  28. view.setOnTouchListener(this);
  29. setContentView(view);
  30. }
  31. class TransformMatrixView extends ImageView
  32. {
  33. private Bitmap bitmap;
  34. private Matrix matrix;
  35. public TransformMatrixView(Context context)
  36. {
  37. super(context);
  38. bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
  39. matrix = new Matrix();
  40. }
  41. @Override
  42. protected void onDraw(Canvas canvas)
  43. {
  44. // 画出原图像
  45. canvas.drawBitmap(bitmap, 0, 0, null);
  46. // 画出变换后的图像
  47. canvas.drawBitmap(bitmap, matrix, null);
  48. super.onDraw(canvas);
  49. }
  50. @Override
  51. public void setImageMatrix(Matrix matrix)
  52. {
  53. this.matrix.set(matrix);
  54. super.setImageMatrix(matrix);
  55. }
  56. public Bitmap getImageBitmap()
  57. {
  58. return bitmap;
  59. }
  60. }
  61. public boolean onTouch(View v, MotionEvent e)
  62. {
  63. if(e.getAction() == MotionEvent.ACTION_UP)
  64. {
  65. Matrix matrix = new Matrix();
  66. // 输出图像的宽度和高度(162 x 251)
  67. Log.e("TestTransformMatrixActivity", "image size: width x height = " +  view.getImageBitmap().getWidth() + " x " + view.getImageBitmap().getHeight());
  68. // 1. 平移
  69. matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
  70. // 在x方向平移view.getImageBitmap().getWidth(),在y轴方向view.getImageBitmap().getHeight()
  71. view.setImageMatrix(matrix);
  72. // 下面的代码是为了查看matrix中的元素
  73. float[] matrixValues = new float[9];
  74. matrix.getValues(matrixValues);
  75. for(int i = 0; i < 3; ++i)
  76. {
  77. String temp = new String();
  78. for(int j = 0; j < 3; ++j)
  79. {
  80. temp += matrixValues[3 * i + j ] + "\t";
  81. }
  82. Log.e("TestTransformMatrixActivity", temp);
  83. }
  84. //          // 2. 旋转(围绕图像的中心点)
  85. //          matrix.setRotate(45f, view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
  86. //
  87. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  88. //          matrix.postTranslate(view.getImageBitmap().getWidth() * 1.5f, 0f);
  89. //          view.setImageMatrix(matrix);
  90. //
  91. //          // 下面的代码是为了查看matrix中的元素
  92. //          float[] matrixValues = new float[9];
  93. //          matrix.getValues(matrixValues);
  94. //          for(int i = 0; i < 3; ++i)
  95. //          {
  96. //              String temp = new String();
  97. //              for(int j = 0; j < 3; ++j)
  98. //              {
  99. //                  temp += matrixValues[3 * i + j ] + "\t";
  100. //              }
  101. //              Log.e("TestTransformMatrixActivity", temp);
  102. //          }
  103. //          // 3. 旋转(围绕坐标原点) + 平移(效果同2)
  104. //          matrix.setRotate(45f);
  105. //          matrix.preTranslate(-1f * view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight() / 2f);
  106. //          matrix.postTranslate((float)view.getImageBitmap().getWidth() / 2f, (float)view.getImageBitmap().getHeight() / 2f);
  107. //
  108. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  109. //          matrix.postTranslate((float)view.getImageBitmap().getWidth() * 1.5f, 0f);
  110. //          view.setImageMatrix(matrix);
  111. //
  112. //          // 下面的代码是为了查看matrix中的元素
  113. //          float[] matrixValues = new float[9];
  114. //          matrix.getValues(matrixValues);
  115. //          for(int i = 0; i < 3; ++i)
  116. //          {
  117. //              String temp = new String();
  118. //              for(int j = 0; j < 3; ++j)
  119. //              {
  120. //                  temp += matrixValues[3 * i + j ] + "\t";
  121. //              }
  122. //              Log.e("TestTransformMatrixActivity", temp);
  123. //          }
  124. //          // 4. 缩放
  125. //          matrix.setScale(2f, 2f);
  126. //          // 下面的代码是为了查看matrix中的元素
  127. //          float[] matrixValues = new float[9];
  128. //          matrix.getValues(matrixValues);
  129. //          for(int i = 0; i < 3; ++i)
  130. //          {
  131. //              String temp = new String();
  132. //              for(int j = 0; j < 3; ++j)
  133. //              {
  134. //                  temp += matrixValues[3 * i + j ] + "\t";
  135. //              }
  136. //              Log.e("TestTransformMatrixActivity", temp);
  137. //          }
  138. //
  139. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  140. //          matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
  141. //          view.setImageMatrix(matrix);
  142. //
  143. //          // 下面的代码是为了查看matrix中的元素
  144. //          matrixValues = new float[9];
  145. //          matrix.getValues(matrixValues);
  146. //          for(int i = 0; i < 3; ++i)
  147. //          {
  148. //              String temp = new String();
  149. //              for(int j = 0; j < 3; ++j)
  150. //              {
  151. //                  temp += matrixValues[3 * i + j ] + "\t";
  152. //              }
  153. //              Log.e("TestTransformMatrixActivity", temp);
  154. //          }
  155. //          // 5. 错切 - 水平
  156. //          matrix.setSkew(0.5f, 0f);
  157. //          // 下面的代码是为了查看matrix中的元素
  158. //          float[] matrixValues = new float[9];
  159. //          matrix.getValues(matrixValues);
  160. //          for(int i = 0; i < 3; ++i)
  161. //          {
  162. //              String temp = new String();
  163. //              for(int j = 0; j < 3; ++j)
  164. //              {
  165. //                  temp += matrixValues[3 * i + j ] + "\t";
  166. //              }
  167. //              Log.e("TestTransformMatrixActivity", temp);
  168. //          }
  169. //
  170. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  171. //          matrix.postTranslate(view.getImageBitmap().getWidth(), 0f);
  172. //          view.setImageMatrix(matrix);
  173. //
  174. //          // 下面的代码是为了查看matrix中的元素
  175. //          matrixValues = new float[9];
  176. //          matrix.getValues(matrixValues);
  177. //          for(int i = 0; i < 3; ++i)
  178. //          {
  179. //              String temp = new String();
  180. //              for(int j = 0; j < 3; ++j)
  181. //              {
  182. //                  temp += matrixValues[3 * i + j ] + "\t";
  183. //              }
  184. //              Log.e("TestTransformMatrixActivity", temp);
  185. //          }
  186. //          // 6. 错切 - 垂直
  187. //          matrix.setSkew(0f, 0.5f);
  188. //          // 下面的代码是为了查看matrix中的元素
  189. //          float[] matrixValues = new float[9];
  190. //          matrix.getValues(matrixValues);
  191. //          for(int i = 0; i < 3; ++i)
  192. //          {
  193. //              String temp = new String();
  194. //              for(int j = 0; j < 3; ++j)
  195. //              {
  196. //                  temp += matrixValues[3 * i + j ] + "\t";
  197. //              }
  198. //              Log.e("TestTransformMatrixActivity", temp);
  199. //          }
  200. //
  201. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  202. //          matrix.postTranslate(0f, view.getImageBitmap().getHeight());
  203. //          view.setImageMatrix(matrix);
  204. //
  205. //          // 下面的代码是为了查看matrix中的元素
  206. //          matrixValues = new float[9];
  207. //          matrix.getValues(matrixValues);
  208. //          for(int i = 0; i < 3; ++i)
  209. //          {
  210. //              String temp = new String();
  211. //              for(int j = 0; j < 3; ++j)
  212. //              {
  213. //                  temp += matrixValues[3 * i + j ] + "\t";
  214. //              }
  215. //              Log.e("TestTransformMatrixActivity", temp);
  216. //          }
  217. //          7. 错切 - 水平 + 垂直
  218. //          matrix.setSkew(0.5f, 0.5f);
  219. //          // 下面的代码是为了查看matrix中的元素
  220. //          float[] matrixValues = new float[9];
  221. //          matrix.getValues(matrixValues);
  222. //          for(int i = 0; i < 3; ++i)
  223. //          {
  224. //              String temp = new String();
  225. //              for(int j = 0; j < 3; ++j)
  226. //              {
  227. //                  temp += matrixValues[3 * i + j ] + "\t";
  228. //              }
  229. //              Log.e("TestTransformMatrixActivity", temp);
  230. //          }
  231. //
  232. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  233. //          matrix.postTranslate(0f, view.getImageBitmap().getHeight());
  234. //          view.setImageMatrix(matrix);
  235. //
  236. //          // 下面的代码是为了查看matrix中的元素
  237. //          matrixValues = new float[9];
  238. //          matrix.getValues(matrixValues);
  239. //          for(int i = 0; i < 3; ++i)
  240. //          {
  241. //              String temp = new String();
  242. //              for(int j = 0; j < 3; ++j)
  243. //              {
  244. //                  temp += matrixValues[3 * i + j ] + "\t";
  245. //              }
  246. //              Log.e("TestTransformMatrixActivity", temp);
  247. //          }
  248. //          // 8. 对称 (水平对称)
  249. //          float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
  250. //          matrix.setValues(matrix_values);
  251. //          // 下面的代码是为了查看matrix中的元素
  252. //          float[] matrixValues = new float[9];
  253. //          matrix.getValues(matrixValues);
  254. //          for(int i = 0; i < 3; ++i)
  255. //          {
  256. //              String temp = new String();
  257. //              for(int j = 0; j < 3; ++j)
  258. //              {
  259. //                  temp += matrixValues[3 * i + j ] + "\t";
  260. //              }
  261. //              Log.e("TestTransformMatrixActivity", temp);
  262. //          }
  263. //
  264. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  265. //          matrix.postTranslate(0f, view.getImageBitmap().getHeight() * 2f);
  266. //          view.setImageMatrix(matrix);
  267. //
  268. //          // 下面的代码是为了查看matrix中的元素
  269. //          matrixValues = new float[9];
  270. //          matrix.getValues(matrixValues);
  271. //          for(int i = 0; i < 3; ++i)
  272. //          {
  273. //              String temp = new String();
  274. //              for(int j = 0; j < 3; ++j)
  275. //              {
  276. //                  temp += matrixValues[3 * i + j ] + "\t";
  277. //              }
  278. //              Log.e("TestTransformMatrixActivity", temp);
  279. //          }
  280. //          // 9. 对称 - 垂直
  281. //          float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
  282. //          matrix.setValues(matrix_values);
  283. //          // 下面的代码是为了查看matrix中的元素
  284. //          float[] matrixValues = new float[9];
  285. //          matrix.getValues(matrixValues);
  286. //          for(int i = 0; i < 3; ++i)
  287. //          {
  288. //              String temp = new String();
  289. //              for(int j = 0; j < 3; ++j)
  290. //              {
  291. //                  temp += matrixValues[3 * i + j ] + "\t";
  292. //              }
  293. //              Log.e("TestTransformMatrixActivity", temp);
  294. //          }
  295. //
  296. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  297. //          matrix.postTranslate(view.getImageBitmap().getWidth() * 2f, 0f);
  298. //          view.setImageMatrix(matrix);
  299. //
  300. //          // 下面的代码是为了查看matrix中的元素
  301. //          matrixValues = new float[9];
  302. //          matrix.getValues(matrixValues);
  303. //          for(int i = 0; i < 3; ++i)
  304. //          {
  305. //              String temp = new String();
  306. //              for(int j = 0; j < 3; ++j)
  307. //              {
  308. //                  temp += matrixValues[3 * i + j ] + "\t";
  309. //              }
  310. //              Log.e("TestTransformMatrixActivity", temp);
  311. //          }
  312. //          // 10. 对称(对称轴为直线y = x)
  313. //          float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
  314. //          matrix.setValues(matrix_values);
  315. //          // 下面的代码是为了查看matrix中的元素
  316. //          float[] matrixValues = new float[9];
  317. //          matrix.getValues(matrixValues);
  318. //          for(int i = 0; i < 3; ++i)
  319. //          {
  320. //              String temp = new String();
  321. //              for(int j = 0; j < 3; ++j)
  322. //              {
  323. //                  temp += matrixValues[3 * i + j ] + "\t";
  324. //              }
  325. //              Log.e("TestTransformMatrixActivity", temp);
  326. //          }
  327. //
  328. //          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
  329. //          matrix.postTranslate(view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth(),
  330. //                  view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth());
  331. //          view.setImageMatrix(matrix);
  332. //
  333. //          // 下面的代码是为了查看matrix中的元素
  334. //          matrixValues = new float[9];
  335. //          matrix.getValues(matrixValues);
  336. //          for(int i = 0; i < 3; ++i)
  337. //          {
  338. //              String temp = new String();
  339. //              for(int j = 0; j < 3; ++j)
  340. //              {
  341. //                  temp += matrixValues[3 * i + j ] + "\t";
  342. //              }
  343. //              Log.e("TestTransformMatrixActivity", temp);
  344. //          }
  345. view.invalidate();
  346. }
  347. return true;
  348. }
  349. }

下面给出上述代码中,各种变换的具体结果及其对应的相关变换矩阵

1.     平移

Android中图像变换Matrix的原理、代码验证和应用(二)

输出的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

请对照第一部分中的“一、平移变换”所讲的情形,考察上述矩阵的正确性。

2.     旋转(围绕图像的中心点)

Android中图像变换Matrix的原理、代码验证和应用(二)

输出的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

它实际上是

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);

这两条语句综合作用的结果。根据第一部分中“二、旋转变换”里面关于围绕某点旋转的公式,

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

所产生的转换矩阵就是:

Android中图像变换Matrix的原理、代码验证和应用(二)

而matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);的意思就是在上述矩阵的左边再乘以下面的矩阵:

Android中图像变换Matrix的原理、代码验证和应用(二)

关于post是左乘这一点,我们在前面的理论部分曾经提及过,后面我们还会专门讨论这个问题。

所以它实际上就是:

Android中图像变换Matrix的原理、代码验证和应用(二)

出去计算上的精度误差,我们可以看到我们计算出来的结果,和程序直接输出的结果是一致的。

3.     旋转(围绕坐标原点旋转,在加上两次平移,效果同2)

Android中图像变换Matrix的原理、代码验证和应用(二)

根据第一部分中“二、旋转变换”里面关于围绕某点旋转的解释,不难知道:

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

等价于

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

其中matrix.setRotate(45f)对应的矩阵是:

Android中图像变换Matrix的原理、代码验证和应用(二)

matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight()/ 2f)对应的矩阵是:

Android中图像变换Matrix的原理、代码验证和应用(二)

由于是preTranslate,是先乘,也就是右乘,即它应该出现在matrix.setRotate(45f)所对应矩阵的右侧。

matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f)对应的矩阵是:

Android中图像变换Matrix的原理、代码验证和应用(二)

这次由于是postTranslate,是后乘,也就是左乘,即它应该出现在matrix.setRotate(45f)所对应矩阵的左侧。

所以综合起来,

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

对应的矩阵就是:

Android中图像变换Matrix的原理、代码验证和应用(二)

这和下面这个矩阵(围绕图像中心顺时针旋转45度)其实是一样的:

Android中图像变换Matrix的原理、代码验证和应用(二)

因此,此处变换后的图像和2中变换后的图像时一样的。

4.     缩放变换

Android中图像变换Matrix的原理、代码验证和应用(二)

程序所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中第二个矩阵,其实是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“三、缩放变换”和“一、平移变换”说法,自行验证结果。

5.     错切变换(水平错切)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,第二个矩阵其实是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

6.     错切变换(垂直错切)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,第二个矩阵其实是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

7.     错切变换(水平+垂直错切)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,后者是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

8.     对称变换(水平对称)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个各矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,后者是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

9.     对称变换(垂直对称)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,后者是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

10.   对称变换(对称轴为直线y = x)

Android中图像变换Matrix的原理、代码验证和应用(二)

代码所输出的两个矩阵分别是:

Android中图像变换Matrix的原理、代码验证和应用(二)

其中,后者是下面两个矩阵相乘的结果:

Android中图像变换Matrix的原理、代码验证和应用(二)

大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

11.   关于先乘和后乘的问题

由于矩阵的乘法运算不满足交换律,我们在前面曾经多次提及先乘、后乘的问题,即先乘就是矩阵运算中右乘,后乘就是矩阵运算中的左乘。其实先乘、后乘的概念是针对变换操作的时间先后而言的,左乘、右乘是针对矩阵运算的左右位置而言的。以第一部分“二、旋转变换”中围绕某点旋转的情况为例:

Android中图像变换Matrix的原理、代码验证和应用(二)

越靠近原图像中像素的矩阵,越先乘,越远离原图像中像素的矩阵,越后乘。事实上,图像处理时,矩阵的运算是从右边往左边方向进行运算的。这就形成了越在右边的矩阵(右乘),越先运算(先乘),反之亦然。

当然,在实际中,如果首先指定了一个matrix,比如我们先setRotate(Android中图像变换Matrix的原理、代码验证和应用(二)),即指定了上面变换矩阵中,中间的那个矩阵,那么后续的矩阵到底是pre还是post运算,都是相对这个中间矩阵而言的。

所有这些,其实都是很自然的事情。