Android 图片处理
来源:互联网 发布:家谱排版软件 编辑:程序博客网 时间:2024/05/24 03:42
图片处理
本文主要介绍Android平台下图片的获取和处理,欢迎点赞,拍砖,留言
图片的加载和创造
- 图片的加载
图片的来源十分的丰富,可以是本地资源文件,也是可以是本地文件,同样也支持对于流的加载。实例如下:
本地资源:BitmapFactory.decodeResource(this.getResources(),R.id.XX,opts)
本地文件:BitmapFactory.decodeFile(“文件路径”)
本地流:BitmapFactory.decodeStream(流);
其中需要重点关注的是它们都可以带有参数来进行加载,这个参数就是BitmapFactory.Options,它可以决定加载的时候是采用什么样的模式!最明显是例子就是在很多的情况下,并不会直接把一张图片加载到内存里面,而是才用先查看图片的大小,然后设定一定的缩放比例进行加载。
查看图片的宽高,需要设置options.inJustDecodeBounds = true;通过options.outHeight;options.outWidth;来获取宽高
缩放是通过option.inJustDecodeBounds = false 并且,ptions.inSampleSize = 缩放的比例
这个地方的option很有点值得考虑的,在Android2.3之前有,设置inPuraeable 参数可以放到匿名的内存区来存放图片。后来就不行了,但是有个inBitmap的参数可以重复利用之前的图片空间。图片加载经典的三级缓存,其中用到了软引用和inBitmap来极大程序的缓解图片的加载问题。
https://developer.android.google.cn/topic/performance/graphics/manage-memory.html
加载出来的图片一般是不可变,就是没法做下面的变换动作。
- 图片的创建
创造一张图片使用的Bitmap.createBitmap()函数,里面需要一些参数指定图片的宽高之类的,其中需要注意是Config这个参数,拿Bitmap.Config.ARGB_8888举例子,它表示图片的每个像素是由A(透明度值),R(红色),G(绿色),B(蓝色)组成,并且每个值用八位表示!其它的参数也大概是这样的意思。
图片的变换
- 利用系统矩阵转换
首先介绍下矩阵的基础知识,Android中用Matrix这个来表示,它是一个3X3的矩阵
public static final int MSCALE_X = 0; public static final int MSKEW_X = 1; public static final int MTRANS_X = 2; public static final int MSKEW_Y = 3; public static final int MSCALE_Y = 4; public static final int MTRANS_Y = 5; public static final int MPERSP_0 = 6; public static final int MPERSP_1 = 7; public static final int MPERSP_2 = 8;
其每个值的意义如上,其实这个是没什么必要的,知道具体的概念后自己可以任意构造。每个像素点是一个3X1的一维矩阵来表示,也许很奇怪,为什么是三维,其实X坐标,Y坐标,以及透视值。需要注意是就是这个透视值,在视觉中,即时是同一张图片你看的远近不一样,其形成的视觉效果也是不一样的。具体的知识可以查看透视图相关。
理论上,我们有了这个矩阵后可以做任意操作,但是Matrix帮你封装了一些基本的操作,这里进行一些演示:
首先需要建立一个可以绘制的位图,如果下所示:
bitmap = Bitmap.createBitmap(具体宽,具体高, Bitmap.Config.ARGB_8888);canvas = new Canvas(bitmap);Paint paint = new Paint();canvas.drawBitmap(temp,0f,0f,paint);imageView.setImageBitmap(bitmap);
进行旋转操作:
//先进行清理,如果是一次性的可以不用Paint paint2 = new Paint();paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawPaint(paint2);paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix matrix = new Matrix();matrix.setRotate(50);paint2.setAntiAlias(true);canvas.drawBitmap(temp,matrix,paint2);imageView.postInvalidate();
进行镜像操作:
Paint paint3 = new Paint();paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));canvas.drawPaint(paint3);paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix matrixXY = new Matrix();matrixXY.setScale(-1,1);//这里进行处理matrixXY.postTranslate((float) temp.getWidth(),0);paint3.setAntiAlias(true);canvas.drawBitmap(temp,matrixXY,paint3);imageView.postInvalidate();
修改透视值,形成视觉上的缩放:
Paint paint4 = new Paint();paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));canvas.drawPaint(paint4);paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix matrixX = new Matrix();matrixX.setValues(new float[]{ 1,0,0, 0,1,0, 0,0,2});paint4.setAntiAlias(true);canvas.drawBitmap(temp,matrixX,paint4);imageView.postInvalidate();
以上的的矩阵变化都是指的空间变换,其实还有一个ColorMatrix用来控制RGBA的属性变换,挺好玩的,大家可以琢磨下,掌握了这些简单的东西后就可以完全的控制一张图片的表示了。(因为后面要黏代码,原理的有时间搁在其它文章里面讲)
- 手动处理图片的像素点
具体到每个像素点的操作,这更应该像C程序来做的事哈!其实为了效率,一般是也是扔给C来做的,不过也可以使用java代码,下面来看两个有意思的代码
高斯模糊:
public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { Bitmap bitmap; if (canReuseInBitmap) { bitmap = sentBitmap; } else { bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); } if (radius < 1) { return (null); } int w = bitmap.getWidth(); int h = bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int r[] = new int[wh]; int g[] = new int[wh]; int b[] = new int[wh]; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin[] = new int[Math.max(w, h)]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[] = new int[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int[][] stack = new int[div][3]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + Math.min(wm, Math.max(i, 0))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = Math.min(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.max(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = Math.min(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); }
打马赛克:
/** * * @param bitmap * @param targetRect * @param blockSize * @return * @throws Exception */ public static Bitmap makeMosaic(Bitmap bitmap, Rect targetRect, int blockSize) throws OutOfMemoryError { if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0 || bitmap.isRecycled()) { throw new RuntimeException("bad bitmap to add mosaic"); } if (blockSize < 4) { blockSize = 4; } if (targetRect == null) { targetRect = new Rect(); } int bw = bitmap.getWidth(); int bh = bitmap.getHeight(); if (targetRect.isEmpty()) { targetRect.set(0, 0, bw, bh); } // int rectW = targetRect.width(); int rectH = targetRect.height(); int[] bitmapPxs = new int[bw * bh]; // 获取图片像素 bitmap.getPixels(bitmapPxs, 0, bw, 0, 0, bw, bh); //目标矩形区域根据块的大小切割出来的行数与列数 int rowCount = (int) Math.ceil((float) rectH / blockSize); int columnCount = (int) Math.ceil((float) rectW / blockSize); //最大的X,Y为图片的宽高 int maxX = bw; int maxY = bh; for (int r = 0; r < rowCount; r++) { // row loop for (int c = 0; c < columnCount; c++) {// column loop int startX = targetRect.left + c * blockSize + 1; int startY = targetRect.top + r * blockSize + 1; dimBlock(bitmapPxs, startX, startY, blockSize, maxX, maxY); } } return Bitmap.createBitmap(bitmapPxs, bw, bh, Bitmap.Config.ARGB_8888); } /** * 从块内取样,并放大,从而达到马赛克的模糊效果 * * @param pxs * @param startX * @param startY * @param blockSize * @param maxX * @param maxY */ private static void dimBlock(int[] pxs, int startX, int startY, int blockSize, int maxX, int maxY) { int stopX = startX + blockSize - 1; int stopY = startY + blockSize - 1; if (stopX > maxX) { stopX = maxX; } if (stopY > maxY) { stopY = maxY; } // int sampleColorX = startX + blockSize / 2; int sampleColorY = startY + blockSize / 2; // if (sampleColorX > maxX) { sampleColorX = maxX; } if (sampleColorY > maxY) { sampleColorY = maxY; } //在像素点数组中所处的行数 int colorLinePosition = (sampleColorY - 1) * maxX; //在像素点数所处的位置 int sampleColor = pxs[colorLinePosition + sampleColorX - 1];// 像素从1开始,但是数组层0开始 //把块中的颜色都已取样的像素点来填充,用块中的像素点取样 for (int y = startY; y <= stopY; y++) { int p = (y - 1) * maxX; for (int x = startX; x <= stopX; x++) { // 像素从1开始,但是数组层0开始 pxs[p + x - 1] = sampleColor; } } }
图片的存储
图片的缓存的三级结构
- 内存缓存* LruCache*(提供map的功能,可以通过key获取和存储图片)
- 文件缓存 内部存储的Cache和外部存储的Cache,或者直接就放在外置SDCard的公共目录下
- 网络 最后的选择才是从网路上请求
一般的使用顺序就是,当想要获取一张图片的时候,先去内存缓存中查找,再去文件查找,都没有的情况下才去请求网络,网络请求的图片保存在文件缓存中。以上内存不是我们关注的重点。跳过,有兴趣的可以交流下!
图片的保存
图片可以压缩成PNG,JPG等格式,在压缩成JPG的时候提供其压缩率,这个值只对JPG有效,因为PNG是无所压缩的。使用的函数:
compress (Bitmap.CompressFormat format,int quality,OutputStream stream),我们可以看到起格式有JPG,PNG,WEBP三种,后面的质量只对JPG有效,一般设置为90即可。输出流一般是用于保存文件路径。
详情请见下回分晓,哇哈哈哈~~
- android图片处理一
- android 图片放大处理
- android 处理图片资源
- Android处理图片
- android图片处理技巧
- android处理图片
- Android图片处理技巧
- Android图片处理技巧
- android图片处理方法
- android 图片处理
- Android 图片处理特效
- android 图片处理
- android 图片处理
- android 图片处理
- android图片处理方法
- android图片处理
- android 处理图片工具
- Android图片处理
- Tensorflow 0.12.0 windows 7, (10可以照做) 安装
- 评标客观分,不“客观一致”导致中标无效是谁的责任
- 石子合并+四边形优化
- 深入理解Service(一)——服务生命周期
- 16.迭代器模式
- Android 图片处理
- React Native的组件ListView
- OpenCV
- 使用 ionic开发-页面跳转
- JVM原理
- Java常用类-Math、BigInteger、BigDecimal
- 纯CSS3图标旋转效果
- burpsuite web请求参数检查插件
- Centos66 编译 Freeswitch mod_shout mp3录音模块