Android图像处理—图像变换处理探究
此篇博客主要研究了通过各种方式来达到对图像形状变换的效果。
矩阵变换基础
图像的变换同样可以通过矩阵来实现。下面将介绍 平移变换、旋转变换 缩放变换以及错切变换四种变换方式。
1.平移变换
当我们要将一个点从x0,y0移动到x,y的时候,可以通过下图的矩阵变换来实现:
当如图的矩阵左乘原坐标矩阵后,就会得到一个平移到x,y的新坐标矩阵。
2.旋转变换
当我们想要将一个图像顺时针旋转θ角时, 通过如下的矩阵运算可以得到旋转后的坐标点的位置x,y。
3.缩放变换
想要将一个图片进行缩放就比较简单了,通过如下矩阵运算即可得到:
4.错切变换
错切变换比较少接触,效果大概是这样:
仍然是通过矩阵运算,将图像的坐标矩阵左乘如图的矩阵,即可得到:
通过矩阵方法调整图像
界面的实现
首先,我们新建一个ImageMatrixView,代码如下,用于显示一个原图和改变后的图像,用于对比变换前后的情况。
public class ImageMatrixView extends View {
private Bitmap mBitmap;
private Matrix mMatrix;
public ImageMatrixView(Context context) {
super(context);
initView();
}
public ImageMatrixView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public ImageMatrixView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
Log.d("ImageMatrixView","initView");
mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic);
setImageMatrix(new Matrix());
}
public void setImageMatrix(Matrix matrix){
mMatrix = matrix;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//方便对比,画两张图片来对比
Log.d("ImageMatrixView","onDraw");
canvas.drawBitmap(mBitmap,0,0,null); //原图
canvas.drawBitmap(mBitmap,mMatrix,null); //矩阵变换后图片
}
}
然后,再用如下的布局文件,以达到和之前颜色变换矩阵处一样的效果。
实现通过矩阵来改变图像
我们在代码中,分别通过两个数组来存储矩阵的值以及EditText,给每个EditText一个初始值,也就是我们的初等矩阵。然后当按下改变按钮后,则获取每个EditText的值并应用在ImageMatrixView中。
public class MatrixActivity extends AppCompatActivity {
private GridLayout mGridLayout;
private ImageMatrixView mImageMatrixView;
private int mEdWidth;
private int mEdHeight;
private float[] mImageMatrix = new float[9];
private EditText[] mEditTexts = new EditText[9];
private Button mBtnChange;
private Button mBtnReset;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_matrix);
mImageMatrixView = findViewById(R.id.matrix_view);
mGridLayout = findViewById(R.id.group);
mBtnChange = findViewById(R.id.btn_change);
mBtnReset = findViewById(R.id.btn_reset);
mGridLayout.post(new Runnable() {
@Override
public void run() {
mEdWidth = mGridLayout.getWidth() / 3;
mEdHeight = mGridLayout.getHeight() / 3;
addEditTexts();
initImageMatrix();
}
});
mBtnChange.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
change();
}
});
mBtnReset.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
initImageMatrix();
Matrix matrix = new Matrix();
matrix.setValues(mImageMatrix);
mImageMatrixView.setImageMatrix(matrix);
mImageMatrixView.invalidate();
}
});
}
private void addEditTexts() {
for (int i = 0; i < 9; i++) {
EditText editText = new EditText(this);
editText.setGravity(Gravity.CENTER);
mGridLayout.addView(editText, mEdWidth, mEdHeight);
mEditTexts[i] = editText;
}
}
private void getImageMatrix() {
for (int i = 0; i < 9; i++) {
EditText editText = mEditTexts[i];
mImageMatrix[i] = Float.parseFloat(editText.getText().toString());
}
}
private void change(){
getImageMatrix();
Matrix matrix = new Matrix();
matrix.setValues(mImageMatrix);
mImageMatrixView.setImageMatrix(matrix);
mImageMatrixView.invalidate();
}
private void initImageMatrix() {
for (int i = 0; i < 9; i++) {
if (i % 4 == 0) {
mEditTexts[i].setText("1");
} else {
mEditTexts[i].setText("0");
}
}
}
}
效果
如图,分别是图像的四种变换后的效果:
平移变换
旋转变换
缩放变换
错切变换
Android API来实现
其实,Android自带了api来给我们实现这四种变换:
- matrix.setRotation() 旋转,参数为角度的值
- matrix.setTranslate() 平移,参数分别为x轴、y轴的偏移量
- matrix.setScale() 缩放,参数为横向与纵向的放大倍数
- matrix.setSkew() 错切,参数为横向与纵向的k值
- post 矩阵组合
前面四种与前文大同小异,但矩阵的组合运算在这里需要了解一下。
矩阵的组合运算
之前用矩阵进行图像变换的运算时,可以通过多种运算来达到图像变换叠加的作用,在这里同样可以,用法就是第一种变换用正常的setXXX方法,而后面的方法则用postXXX的方法即可,如:
matrix.setScale(2,2);
matrix.postTranslate(200,200);
Xfermode画笔风格处理图片
图层混合结构图
里面的第三张图以后的都是通过Src与Dst通过不同的混合形成的不同效果。
使用Xfermode将图片处理为圆角矩形
使用Xfermode之前,我们最好先将硬件加速关闭。通过setLayerType这个方法,传入LAYER_TYPE_SOFTWARE这个参数,第二个参数传入0,来禁用掉硬件加速。
这里,我们新建一个RoundXferModeView,目的是制作一个以圆角矩形的方式显示图片的View,首先让它继承自View,在里面定义一个原始的Bitmap以及处理后的Bitmap,以及一个画笔。之所以定义两个Bitmap是因为从资源文件获得的Bitmap是默认不可修改的,因此需要一个新的Bitmap来保存。
在initView中,我们做了一些之前颜色变换用到的初始化工作,如新建Canvas等等。通过我们之前的图层混合结构图可以知道,当两张图片进行混合的时候,如果我们选择了SrcIn这个模式,取的就是两张图片交集的部分。如果我们下面绘制一个圆角矩形,而上面绘制一张图片,则产生的效果就是一个圆角矩形的图片。(在Android的设计中,第一个绘制的就是Dst,第二个则是Src)因此,我们先在Canvas上绘制一个圆角矩形,之后再为mPaint调用setXfermode方法,传入一个PorterDuffDuffXfermode,将我们的SRC_IN模式传递进去。最后在Canvas中绘制我们的Bitmap。
public class RoundXferModeView extends View {
private Bitmap mBitmap;
private Bitmap mOutBitmap;
private Paint mPaint;
public RoundXferModeView(Context context) {
super(context);
initView();
}
public RoundXferModeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public RoundXferModeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
setLayerType(LAYER_TYPE_SOFTWARE,null);
mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.hotel); //获取原始Bitmap
mOutBitmap = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(),Config.ARGB_8888);
Canvas canvas = new Canvas(mOutBitmap);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
canvas.drawRoundRect(0,0,mOutBitmap.getWidth(),mOutBitmap.getHeight(),50,50,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(mBitmap,0,0,mPaint);
mPaint.setXfermode(null);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mOutBitmap,0,0,null);
}
}
效果如图:
Shader风格处理图片
主要为了达成渐变的效果,有诸如线性渐变,环形渐变,扫描渐变,组合渐变等。虽然名字不同,显示的效果也不同,但是它们都属于颜色的一种渐变效果。而只有BitmapShader比较特殊,它属于图像渐变。通过BitmapShader,我们同样可以实现一个圆角矩形的图片显示View。
使用BitmapShader将图片处理为圆角矩形
实际上BitmapShader就是以Bitmap为背景绘制某个形状。
这里我们新建了一些变量后,直接重写onDraw方法。初始化了bitmap以及paint后,在初始化BitmapShader时我们传入bitmap,并把x,y轴都设置为CLAMP模式,然后为Painter设置这个shader。最后绘制一个圆,然后将画笔设置为绑定了shader的paint。
BitmapShader的三种模式
- CLAMP 拉伸(这里的拉伸是以最后一个像素点的颜色来填充)
- REPEAT 重复
- MIRROR 镜像
public class BitmapShaderView extends View {
private Bitmap mBitmap;
private Paint mPaint;
private BitmapShader mBitmapShader;
public BitmapShaderView(Context context) {
super(context);
}
public BitmapShaderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BitmapShaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic2);
mBitmapShader = new BitmapShader(mBitmap,TileMode.CLAMP,TileMode.CLAMP);
mPaint.setShader(mBitmapShader);
canvas.drawCircle(300,200,300,mPaint);
}
}
效果如图:
广告时间
我是N0tExpectErr0r,一名广东工业大学的大二学生
欢迎来到我的个人博客,所有文章均在个人博客中同步更新哦
http://blog.N0tExpectErr0r.cn