# ImageStitching
图像矩阵实现,图片拼接、缩放、旋转等工能
## 一、前文<br/>
之前有个朋友委托我实现一个图片拼接的组件,感觉挺有意思,于是周末花了些时间去研究了下,其实拼接这一步并不难,但是我在研究中发现了Matrix这个东西,非常好的东西。为此,我竟然拾起了多年没有动过的线性代数。<br/>
## 二、原理
要彻底搞懂matrix还是需要一定的线性代数上面的理解,不过对于基本使用,了解到矩阵乘法就足够了。<br/>
在android坐标系中,分为x、y和z三个轴,分别代表了长、宽、高三个维度。如下图所示<br/>
![android坐标系](https://user-gold-cdn.xitu.io/2017/12/21/160781ac13ec4e8b?w=527&h=238&f=jpeg&s=8064)
<br/>
在android中,使用三维坐标(x,y,z)组成一个行列式与一个三阶行列式进行矩阵乘法。<br/>
![矩阵乘法](https://user-gold-cdn.xitu.io/2017/12/21/160781ac106891a8?w=393&h=222&f=png&s=6619)
<br/>
图中显示的使用初始坐标组成的矩阵与单位矩阵进行矩阵乘法。矩阵乘法使用可以参考[矩阵乘法](https://en.wikipedia.org/wiki/Matrix_(mathematics))<br/>
Martix会把输入进来的矩阵带入到其内部的矩阵中进行计算,最终输出新的矩阵,来达到对图形形态的处理。<br/>
## 三、基本方法的使用
Matrix提供的基本方法有三种模式,<br/>
1. setXXX()方法,例如 setRotate(),setScale()
2. preXXX()方法,例如 preRotate(),preScale()
3. postXXX()方法,例如 postRotate(),postScale()
<br/>
其中,setXXX()会先将矩阵重置为单位矩阵,然后再进行矩阵变幻<br/>
preXXX()和postXXX()方法会牵扯到矩阵的前乘和后乘,如果了解了矩阵乘法规则,就会明白矩阵前乘和后乘得出来的结果是不一样的,不过一般情况下都会选择使用post方法,后乘。<br/>
其中还有扩展方法比如:<br/>
1. mapRect(rect) / mapRect(desRect,orgRect)<br/>
第一个方法即使用原始矩阵代入运算,会将返回的矩阵直接覆盖在传入的矩阵中<br/>
第二个方法则是对于需要保存原始矩阵的情况下,会把原始矩阵的计算结果赋值到指定的矩阵中
2. setRectToRect(src,des,stf)<br/>
这个方法相当于将原始矩阵填充到目标矩阵中,所以也就要求两个矩阵都是有值的。其中填充模式由第三个参数决定。
```java
/**
* 独立缩放X和Y,直到和src的rect和目标rect确切的匹配。这可能会改变原始rect的宽高比
*/
FILL(0),
/**
* 在保持原有宽高比的情况下计算出一个合适的缩放比例,但也会确保原始rect合适的填入目标rect,
* 最终会把开始的一个边与目标的开始边左边对齐
*/
START(1),
/**
* 与START类似,不过最终结果会尽可能居中
*/
CENTER(2),
/**
* 与START类似,不过最终结果会尽可能靠右边
*/
END(3);
```
3. invert(inverse)<br/>
反转矩阵,可以应用到类似倒影一类的实现中
4. setPolyToPoly(src,srcIndex,dst,dstIndex,pointCount)
这是一个比较神奇的方法。随着pointCount点数量,可以对原始矩阵进行平移、旋转、错切、翻页效果。功能非常强大。
<br/><br/>
此外,关于Matrix还有颜色变幻等效果,更多扩展用法后面会讲到。
## 四、实践到自定义view中
写一个自定义view,最重要的是要了解view的绘制过程。简单的绘制流程如下<br/>
![view绘制流程](https://user-gold-cdn.xitu.io/2017/12/21/160781ac13fca60e?w=683&h=780&f=png&s=12052)
<br/>
其中不带on的方法都为调度方法,不可被重写,这些方法里面会把前期一些必要的数据准备出来,带on前缀的方法都是实际进行处理的方法。<br/>
measure方法是测量控件大小的,layout是用来布局,根据measure测量的结果,把其中每个元素在其内部进行位置的计算。最后会执行的draw方法,draw也分为draw和onDraw,可以根据自己需求来改写对应的方法。<br/>
其中,onMeasure的方法如下所示:<br/>
```java
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// measure child img
final int maxImgWidth = getMeasuredWidth();
final int maxImgHeight = getMeasuredHeight();
final int measureWidthSize = MeasureSpec.getSize(widthMeasureSpec);
final int measureHeightSize = MeasureSpec.getSize(heightMeasureSpec);
int totalImageHeight = 0;
// 缩放和旋转影响size的交给measure
for (int i = 0; i < imgList.size(); i++) {
ImageData imageData = imgList.get(i);
final int imgOrgWidth = imageData.getImgWidth();
final int imgOrgHeight = imageData.getImgHeight();
int imgRotateWidth;
int imgRotateHeight;
if (imageData.scale > 0) {
imageData.matrix.setScale(imageData.scale, imageData.scale);
} else {
final float sizeProportion = (float) imgOrgWidth / imgOrgHeight;
if (imgOrgHeight > imgOrgWidth) {
if (measureHeightSize == MeasureSpec.EXACTLY &&
imgOrgHeight > maxImgHeight) {
imgRotateWidth = (int) (maxImgHeight * sizeProportion);
imgRotateHeight = maxImgHeight;
} else {
imgRotateWidth = imgOrgWidth;
imgRotateHeight = imgOrgHeight;
}
} else {
if (imgOrgWidth > maxImgWidth) {
imgRotateHeight = (int) (maxImgWidth / sizeProportion);
imgRotateWidth = maxImgWidth;
} else {
imgRotateWidth = imgOrgWidth;
imgRotateHeight = imgOrgHeight;
}
}
// resize
imageData.reSize(imgRotateWidth, imgRotateHeight);
}
// rotate
imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);
imageData.matrix.postRotate(imageData.rotateAngle, imageData.drawRect.centerX(),
imageData.drawRect.top + (imageData.drawRect.height() * 0.5f));
imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);
totalImageHeight += imageData.drawRect.height();
}
switch (measureHeightSize) {
// wrap_content
case MeasureSpec.AT_MOST:
setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,
measureWidthSize), MeasureSpec.makeMeasureSpec(totalImageHeight,
measureHeightSize));
break;
// match_parent or accurate num
case MeasureSpec.EXACTLY:
setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,
measureHeightSize));
break;
case MeasureSpec.UNSPECIFIED:
setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,
measureWidthSize), MeasureSpec.makeMeasureSpec(totalImageHeight,
measureHeightSize));
break;
}
}
```
<br/>
所有影响尺寸计算相关的方法都会放到这个measure里面进行计算,比如scale和rotate,都会影响size大小。所以在这里计算完成后,好在layout中进行正确的布局。<br/>
layout中的代码如下:<br/>
```java
@Override
protected void onLayout(boolean changed, int left, int
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
Matrix提供的基本方法有三种模式, setXXX()方法,例如 setRotate(),setScale() preXXX()方法,例如 preRotate(),preScale() postXXX()方法,例如 postRotate(),postScale() 其中,setXXX()会先将矩阵重置为单位矩阵,然后再进行矩阵变幻 preXXX()和postXXX()方法会牵扯到矩阵的前乘和后乘,如果了解了矩阵乘法规则,就会明白矩阵前乘和后乘得出来的结果是不一样的,不过一般情况下都会选择使用post方法,后乘。 其中还有扩展方法比如: 1. mapRect(rect) / mapRect(desRect,orgRect) 第一个方法即使用原始矩阵代入运算,会将返回的矩阵直接覆盖在传入的矩阵中 第二个方法则是对于需要保存原始矩阵的情况下,会把原始矩阵的计算结果赋值到指定的矩阵中 2. setRectToRect(src,des,stf) 这个方法相当于将原始矩阵填充到目标矩阵中,所以也就要求两个矩阵都是有值的。其中填充模式由第三个参数决定。
资源推荐
资源详情
资源评论
收起资源包目录
ImageStitching-master.zip (85个子文件)
ImageStitching-master
gradle.properties 730B
gradle
wrapper
gradle-wrapper.jar 52KB
gradle-wrapper.properties 230B
app
src
androidTest
java
com
kongdy
imagestitching
ExampleInstrumentedTest.kt 652B
test
java
com
kongdy
imagestitching
ExampleUnitTest.kt 350B
main
ic_done-web.png 11KB
ic_zoom_up-web.png 17KB
java
com
kongdy
imagestitching
MainActivity.kt 2KB
res
mipmap-xxhdpi
ic_zoom_out.png 3KB
ic_rotate.png 3KB
ic_launcher_round.png 10KB
ic_zoom_up.png 3KB
ic_add.png 491B
ic_done.png 2KB
ic_launcher.png 6KB
mipmap-hdpi
ic_zoom_out.png 1KB
ic_rotate.png 1KB
ic_launcher_round.png 5KB
ic_zoom_up.png 1KB
ic_add.png 238B
ic_done.png 568B
ic_launcher.png 3KB
drawable-v24
ic_launcher_foreground.xml 2KB
mipmap-anydpi-v26
ic_launcher.xml 272B
ic_launcher_round.xml 272B
mipmap-mdpi
ic_zoom_out.png 817B
ic_rotate.png 792B
ic_launcher_round.png 3KB
ic_zoom_up.png 859B
ic_add.png 236B
ic_done.png 500B
ic_launcher.png 2KB
mipmap-xxxhdpi
ic_zoom_out.png 4KB
ic_rotate.png 4KB
ic_launcher_round.png 15KB
ic_zoom_up.png 5KB
ic_add.png 681B
ic_done.png 3KB
ic_launcher.png 9KB
mipmap-xhdpi
ic_zoom_out.png 2KB
img_3.png 93KB
ic_rotate.png 2KB
ic_launcher_round.png 7KB
ic_zoom_up.png 2KB
img_4.png 82KB
ic_add.png 344B
ic_done.png 1KB
ic_launcher.png 4KB
img_1.png 372KB
img_2.png 401KB
values
colors.xml 208B
strings.xml 77B
styles.xml 383B
layout
activity_main.xml 3KB
drawable
ic_launcher_background.xml 5KB
ic_add-web.png 3KB
ic_zoom_out-web.png 17KB
AndroidManifest.xml 879B
ic_rotate-web.png 16KB
proguard-rules.pro 751B
build.gradle 1KB
.gitignore 7B
gradlew.bat 2KB
build.gradle 644B
.idea
runConfigurations.xml 564B
markdown-navigator
profiles_settings.xml 104B
vcs.xml 180B
misc.xml 2KB
modules.xml 518B
gradle.xml 689B
encodings.xml 200B
markdown-navigator.xml 4KB
settings.gradle 37B
imagestitchinglib
src
androidTest
java
com
kongdy
imagestitchinglib
ExampleInstrumentedTest.java 762B
test
java
com
kongdy
imagestitchinglib
ExampleUnitTest.java 406B
main
java
com
kongdy
imagestitchinglib
view
ImageStitchingView.java 20KB
util
BitmapUtils.java 1KB
res
values
strings.xml 80B
AndroidManifest.xml 115B
proguard-rules.pro 751B
build.gradle 808B
.gitignore 7B
gradlew 5KB
.gitignore 118B
README.md 23KB
共 85 条
- 1
资源评论
十小大
- 粉丝: 1w+
- 资源: 2555
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功