几何操作 ---- 图像的基本操作
完成对图像的基本操作:
1、打开一张图片并将图片显示在界面上。
2、采用进度条控制图片的缩放,旋转,亮度和对比度调节。
那么比较重要的技术点在于:
1、掌握从图库中返回图像的操作,学会将图放置在ImageView控件上,学会使用画笔、画布类作图。
2、掌握setScale、setRotate的使用,熟悉亮度和对比度系数矩阵。
3、了解Intent显式的启动相册Activity的操作,并与原Activity进行通信。
Android提供了API文档,查阅文档得到类中方法的接口和demo。
本教程所使用的主要类包括:
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.graphics.Canvas;
android.graphics.ColorMatrix;
android.graphics.ColorMatrixColorFilter;
android.graphics.Matrix;
android.graphics.Paint;
android.widget.SeekBar;
android.widget.SeekBar.OnSeekBarChangeListener;
1.Android如何获得图像资源?
Android获得图像资源有三种常用的方法:
1.图片放在sdcard中,Bitmap imageBitmap = BitmapFactory.decodeFile(path) ,其中path 是图片的绝对路径;
2. 图片在项目的res-->drawable文件夹下,Drawabledrawable = getResource().getDrawable(R.drawable.pic);
3. 通过显式意图,从拍照或者图库中返回图像。
public void onClick(View v) {
Intent choosephotoIntent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//打开图库文件
startActivityForResult(choosephotoIntent,1);//图库返回结果
}
在onActivityResult(requestCode, resultCode, data)方法中添加
Bitmap cameraBitmap = (Bitmap) data.getExtras().get("data");//得到图片
2.图像几何操作:
数字图像的几何变换是计算机图像处理领域的一个重要课题,它使原始图像按照实际需要产生大小、形状或者位置的变化。根据变换的性质,图像的几何变换可分为位置变换(平移、镜像、旋转)、形状变换(缩放、截取、错切)和复合变换等。
在Android API中有一个Matrix(矩阵)类,可以完成对当前位图对象的重新绘制,也可以绘制一个全新的位图对象。在进行图像的空间的几何变换时可以选择使用该类,完成对图像的旋转、裁剪、缩放或是更改图像的坐标空间。Matrix类是有9个数字的数组来表示转换的。数字可以手动输入,也可以通过进度条控制输入。
简单举例:
Matrixmatrixzoom = new Matrix();
matrixzoom.setValues(new float[]{
.5f,0,0,
0,1,0,
0,0,1
});
canvas.drawBitmap(bmp,matrixzoom, paint);
通过手动设置缩放矩阵,完成对图像的操作。操作的结果是在x轴上图像被压缩了50%,由此可见第一行的.5f影响了x的坐标值,使图像被压缩了50%。若同时将第二行第二列的1改为.5f,则图像在x轴和y轴方向被同时压缩50%,如果改为2,则为扩展一倍。
Matrix matrixzoom = new Matrix();
matrixzoom.setValues(new float[]{
1,.5f,0,
0,1,0,
0,0,1
});
canvas.drawBitmap(bmp,matrixzoom, paint);
操作的结果是导致图像的倾斜,因为第一行第二列的.5f使得每个像素的x值需要根据像素的y值进行改变,即X = X+0.5Y,当y值增加时,x值也会随之增加,空出来的部分用黑色补齐,图片就产生了倾斜的效果。
如果不想通过自己手动修改矩阵来使图片缩放,可以采用下面的内置方法实现:
Matrix matrixzoom = newMatrix();
matrixzoom.setScale(.5f,.5f);
canvas.drawBitmap(bmp,matrixzoom, paint);
此操作的效果是对图片整体进行了50%的缩放,其中第一个参数控制X轴,第二个参数控制Y轴。
Matrix matrixrotate = newMatrix();
matrixrotate.setRotate(30);
canvas.drawBitmap(bmp,matrixrotate, paint);
操作的结果是图像产生了30度的顺时针旋转,如果设置为负数则会逆时针旋转图像,旋转的中心为左上角。
matrixrotate.setRotate(30,bmp.getWidth()/2,bmp.getHeight()/2);
操作可以保证图像的中心点是旋转点。
3.图像颜色值处理:
类似于Matrix对象的使用方法,Android提供了一个ColorMatrix类库,这个类可以改变Paint对象。ColorMatrix也是一个数字数组,不同于操作x、y、z坐标,它操作的是RGB颜色值和Alpha值。
在ColorMatrix中共包含20个浮点数,第一行包含了在单个像素的红色部分上发生的操作,第二行影响了绿色部分,第三行影响蓝色部分,最后一行的操作数可控制像素的Alpha值。
float lightness= 20;
ColorMatrixmatrixlightness = newColorMatrix();
matrixlightness.set(new float[]{//设计亮度矩阵
1,0,0,0,lightness,
0,1,0,0,lightness,
0,0,1,0,lightness,
0,0,0,1,0
});
paint.setColorFilter(newColorMatrixColorFilter(matrixlightness));
Matrixmatrix_lightness = newMatrix();
canvas.drawBitmap(bmp,matrix_lightness, paint);
此操作的结果是可提升图片亮度。
float contrast = 3;
ColorMatrixmatrixcontrast = newColorMatrix();
matrixcontrast.set(new float[]{//对比度矩阵
contrast,0,0,0,0,
0,contrast,0,0,0,
0,0,contrast,0,0,
0,0,0,1,0
});
paint.setColorFilter(newColorMatrixColorFilter(matrixcontrast));
Matrixmatrix_contrast = newMatrix();
canvas.drawBitmap(bmp,matrix_contrast, paint);
此操作的结果是提升了图片的对比度。
4.SeekBar设计:
通常我们采用进度条seekbar来控制一个控件的进度。在这里,我们可以用来控制缩放倍数、旋转角度和亮度、对比度的值。
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/seekBar2"
android:max="255"
android:progress="50" />
默认seekbar的最大值为100,Android:max可以重新设定最大值,Android:progress可以设定当前值。
seekbar1.setOnSeekBarChangeListener(newOnSeekBarChangeListener() {
public voidonProgressChanged(SeekBar arg0, intarg1, boolean arg2) {
float zoom= (float) arg1 / 20;
Matrixmatrixzoom = new Matrix();
matrixzoom.setScale(zoom,zoom);//通过构建缩放矩阵完成功能
canvas.drawBitmap(bitmap,matrixzoom, paint); }
public voidonStartTrackingTouch(SeekBar arg0) {
}
public voidonStopTrackingTouch(SeekBar arg0) {
}
在Mainactivity中对seekbar进行动作设置。为Seekbar设置一个侦听器,当进度条被拖动时,即执行onProgressChanged方法。对onProgressChanged方法进行复写,arg1表示当前拖动条进度,赋值给zoom变量,用来控制缩放的倍数。
onStartTrackingTouch在按下拖动条瞬间执行,onStopTrackingTouch在松开拖动条的瞬间执行。
如果想要实现亮度和对比度的同时变化,矩阵应该怎样构成呢?
结合了对比度和亮度矩阵,构成方法是这样的,同时调节可以调节图片的多种曝光效果:
matrix.set(new float[]{//对比度亮度矩阵
contrast,0,0,0,lightness,
0,contrast,0,0,lightness,
0,0,contrast,0,lightness,
0,0,0,1,0
});
----------------------讲解的部分就到这里,下面开始要发干货了,小伙伴们准备接招!----------------------------
MainActivity.java部分
//功能:实现对原图的编辑:缩放、旋转、亮度、对比度的调节。
public class MainActivity extends Activity {
private SeekBar sb1 = null;
private SeekBar sb2 = null;
private SeekBar sb3 = null;
private SeekBar sb4 = null;
private Button choosephoto = null;
private ImageView originalphoto = null;
private ImageView secondphoto = null;
private Bitmap bmp = null;
private Bitmap bmp1;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
choosephoto = (Button) this.findViewById(R.id.Bton1);// 得到控件
originalphoto = (ImageView) this.findViewById(R.id.ImgView1);
secondphoto = (ImageView) this.findViewById(R.id.ImgView2);
choosephoto.setOnClickListener(new View.OnClickListener() {// 为“打开图片”按钮设置动作,采用匿名内部类的方式,此方式虽然书写方便但是造成代码混乱。
@Override
public void onClick(View v) {
Intent choosephotoIntent = new Intent(//打开一个显式意图
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);// 打开图库文件
startActivityForResult(choosephotoIntent, 1);// 图库返回结果
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {// 对返回结果的处理:各种对图像的编辑操作
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Uri imageuri = data.getData();
System.out.println("the Uri is " + imageuri);
Display currentDisplay = getWindowManager().getDefaultDisplay();// 为了能快速加载图片,需要将原图缩放到合适大小,“合适”的标准由宽高wratio和hratio来决定。
int dw = currentDisplay.getWidth() / 2 - 20;
int dh = currentDisplay.getHeight() / 2 - 10;
System.out.println("the dw is " + dw);
System.out.println("the dh is " + dh);
try {
BitmapFactory.Options bmpopt = new BitmapFactory.Options();//
bmpopt.inJustDecodeBounds = true;
System.out.println("bitmap is decode ");
bmp = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(imageuri), null, bmpopt);
int hratio = (int) Math.ceil(bmpopt.outHeight / (float) dh);// 率=图片高/屏幕高
int wratio = (int) Math.ceil(bmpopt.outWidth / (float) dw);// 率=图片宽/屏幕宽
System.out.println("the hratio is " + hratio);
System.out.println("the wratio is " + wratio);
if (hratio > 1 && wratio > 1) {// 判断得到合适的缩放比率
if (hratio >= wratio) {
bmpopt.inSampleSize = hratio;
} else {
bmpopt.inSampleSize = wratio;
}
}
bmpopt.inJustDecodeBounds = false;
bmp = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(imageuri), null, bmpopt);
originalphoto.setImageBitmap(bmp);// 显示经过缩放后的图库原图片
bmp1 = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(),
bmp.getConfig());// 创建新位图
final Canvas canvas = new Canvas(bmp1);// 画布
final Paint paint = new Paint();// 画笔
paint.setAntiAlias(true);// 消除锯齿
canvas.drawBitmap(bmp, 0, 0, paint);
secondphoto.setImageBitmap(bmp1);// 显示新位图,初始时和原图一致
sb1 = (SeekBar) findViewById(R.id.seekBar1);// 得到四个控制条控件
sb2 = (SeekBar) findViewById(R.id.seekBar2);
sb3 = (SeekBar) findViewById(R.id.seekBar3);
sb4 = (SeekBar) findViewById(R.id.seekBar4);
sb1.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {// 设置控制条操作
@Override
public void onProgressChanged(SeekBar arg0, int arg1,
boolean arg2) {
float zoom = (float) arg1 / 64;
Matrix matrixzoom = new Matrix();
matrixzoom.setValues(new float[] { .5f, 0, 0, 0, 1, 0,
0, 0, 1 });
matrixzoom.setScale(zoom, zoom);// 通过构建缩放矩阵完成功能
canvas.drawBitmap(bmp, matrixzoom, paint);
secondphoto.setImageBitmap(bmp1);
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
});
sb2.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1,
boolean arg2) {
float rotate = (float) arg1 * 360 / 255;
Matrix matrixrotate = new Matrix();
matrixrotate.setRotate(rotate, bmp.getWidth() / 2,
bmp.getHeight() / 2);// 设置旋转操作,并约束了宽高
canvas.drawBitmap(bmp, matrixrotate, paint);
secondphoto.setImageBitmap(bmp1);
}
});
sb3.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1,
boolean arg2) {
float lightness = (arg1 - 128) / 3;
ColorMatrix matrixlightness = new ColorMatrix();
matrixlightness.set(new float[] {// 设计亮度矩阵
1, 0, 0, 0, lightness, 0, 1, 0, 0, lightness,
0, 0, 1, 0, lightness, 0, 0, 0, 1, 0 });
paint.setColorFilter(new ColorMatrixColorFilter(
matrixlightness));
Matrix matrix_lightness = new Matrix();
canvas.drawBitmap(bmp, matrix_lightness, paint);// 很不幸,这里的亮度和对比度只能单独针对原图变化,而不能混合使用。
secondphoto.setImageBitmap(bmp1);
}
});
sb4.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1,
boolean arg2) {
float contrast = (float) arg1 / 16;
ColorMatrix matrixcontrast = new ColorMatrix();
matrixcontrast.set(new float[] {// 对比度矩阵
contrast, 0, 0, 0, 0, 0, contrast, 0, 0, 0, 0,
0, contrast, 0, 0, 0, 0, 0, 1, 0 });
paint.setColorFilter(new ColorMatrixColorFilter(
matrixcontrast));
Matrix matrix_contrast = new Matrix();
canvas.drawBitmap(bmp, matrix_contrast, paint);
secondphoto.setImageBitmap(bmp1);
}
});
} catch (FileNotFoundException e) {// 捕获异常的操作
Log.v("error", e.toString());
}
}
}
}
布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/textView1"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:text="@string/zoom"
android:textColor="#638"
android:textStyle="bold" />
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/seekBar2"
android:max="255"
android:progress="50"
android:secondaryProgress="75" />
<TextView
android:id="@+id/textView2"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/seekBar2"
android:text="@string/rotate"
android:textColor="#638"
android:textStyle="bold" />
<SeekBar
android:id="@+id/seekBar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/seekBar1"
android:layout_toRightOf="@+id/textView2"
android:max="255"
android:progress="50"
android:secondaryProgress="75" />
<TextView
android:id="@+id/textView3"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/seekBar3"
android:text="@string/brightness"
android:textColor="#638"
android:textStyle="bold" />
<SeekBar
android:id="@+id/seekBar3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/seekBar2"
android:layout_toRightOf="@+id/textView3"
android:max="255"
android:progress="50"
android:secondaryProgress="75" />
<TextView
android:id="@+id/textView4"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/seekBar4"
android:text="@string/contrast"
android:textColor="#638"
android:textStyle="bold" />
<SeekBar
android:id="@+id/seekBar4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/seekBar3"
android:layout_toRightOf="@+id/textView4"
android:max="100"
android:progress="20"
android:secondaryProgress="30" />
<ImageView
android:id="@+id/ImgView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:layout_alignParentLeft="true"
android:background="#444" />
<ImageView
android:id="@+id/ImgView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:layout_alignParentRight="true"
android:background="#eee" />
<Button
android:id="@+id/Bton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/seekBar4"
android:text="@string/bt1" />
</RelativeLayout>
Manifest.xml文件中别忘了添加:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
项目完成!