Android 图片处理笔记
2021-07-26 本文已影响0人
silencefun
一、插值法放大
参考【图像缩放】双立方(三次)卷积插值(Android版改写)
思想是
1.拿到原始图的所有像素点的,每个点的ARGB具体数值放到数组里。
2.按照比例关系算出目标尺寸图对应点的对应像素点然后取原始图的像素点数据。
但是原方案,需要大量内存,来回倒腾,小图还可以,
大图4k
内存占用 = bitmap width * bitmap height * 4;
384021604/1024/1024=31.640625MB,
反复申请,折腾三下不得内存挤爆了。
1.优化:直接计算偏移量就好,
public static boolean imgScaleNear2(byte[] srcbuffer, byte[] desBuffer, int srcW, int srcH, int destW, int destH) {
float rowRatio = ((float) srcH) / ((float) destH);
float colRatio = ((float) srcW) / ((float) destW);
for (int row = 0; row < destH; row++) {
int srcRow = Math.round(((float) row) * rowRatio);
if (srcRow >= srcH) {
srcRow = srcH - 1;
}
for (int col = 0; col < destW; col++) {
int srcCol = Math.round(((float) col) * colRatio);
if (srcCol >= srcW) {
srcCol = srcW - 1;
}
int inpos = (srcRow*srcW +srcCol)*4;
int outpos = (row *destW+ col)*4;
//填充部分
desBuffer[outpos] = srcbuffer[inpos];
desBuffer[outpos + 1] = srcbuffer[inpos + 1];
desBuffer[outpos + 2] = srcbuffer[inpos + 2];
desBuffer[outpos + 3] = srcbuffer[inpos + 3];
//Log.e("tai","col=="+col+"=="+"row=="+row+"==");
}
}
return false;
}
2.使用
int bytes = bitmap.getByteCount(); //返回可用于储存此位图像素的最小字节数
ByteBuffer buffer = ByteBuffer.allocate(bytes); // 使用allocate()静态方法创建字节缓冲区
bitmap.copyPixelsToBuffer(buffer); // 将位图的像素复制到指定的缓冲区
bitmap.recycle();
ByteBuffer desBuffer = ByteBuffer.allocate(dWidth * dHeight * 4);
buffer.position(0);
desBuffer.position(0);
BitmapUtils.imgScaleNear2(buffer.array(), desBuffer.array(), sWidth, sHeight, dWidth, dHeight);
buffer.clear();
desBuffer.position(0);
Bitmap outbitmap = Bitmap.createBitmap(dWidth, dHeight, Bitmap.Config.ARGB_8888);
outbitmap.copyPixelsFromBuffer(desBuffer);
desBuffer.clear();
savePicture(outbitmap);
outbitmap.recycle();
3. buffer.position(0)作用。
RuntimeException: Buffer not large enough for pixels
buffer.position(0);//将buffer的下一读写位置置为0。
4.配置清单申请更大的Heap
android:largeHeap="true"
8k的图片一张占
768043204/1024/1024=126.5625Mb
不申请是完全不够的,大部分手机设备系统正常分配的内存最多为192M;当设置largeHeap时,最多可申请512M。
打印测试工具
private void printCurrentMemory(String tag) {
final Runtime runtime = Runtime.getRuntime();
final long usedMemInMB = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB = runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;
Log.e(TAG, tag + ",usedMemInMB=" + usedMemInMB + ",maxHeapSizeInMB=" + maxHeapSizeInMB + ",availHeapSizeInMB=" + availHeapSizeInMB);
}
二、照片添加坐标信息
直接写进入是不行的要转换成度分秒格式
/**
* 浮点型经纬度值转成度分秒格式
*
* @param coord
* @return
*/
public static String decimalToDMS(double coord) {
String output, degrees, minutes, seconds;
double mod = coord % 1;
int intPart = (int) coord;
degrees = String.valueOf(intPart);
coord = mod * 60;
mod = coord % 1;
intPart = (int) coord;
if (intPart < 0) {
intPart *= -1;
}
minutes = String.valueOf(intPart);
coord = mod * 60;
intPart = (int) coord;
if (intPart < 0) {
intPart *= -1;
}
seconds = String.valueOf(intPart);
output = degrees + "/1," + minutes + "/1," + seconds + "/1";
return output;
}
/**
* 将经纬度信息写入JPEG图片文件里
*
* @param picPath JPEG图片文件路径
* @param dLat 纬度
* @param dLon 经度
*/
public static void writeLatLonIntoJpeg(String picPath, double dLat, double dLon) {
File file = new File(picPath);
if (file.exists()) {
try {
ExifInterface exif = new ExifInterface(picPath);
String tagLat = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
String tagLon = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
if (tagLat == null && tagLon == null) {
// 无经纬度信息
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, decimalToDMS(dLat));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, dLat > 0 ? "N" : "S"); // 区分南北半球
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, decimalToDMS(dLon));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, dLon > 0 ? "E" : "W"); // 区分东经西经
exif.saveAttributes();
}
exif.setAttribute(ExifInterface.TAG_MODEL, Build.MODEL);
} catch (Exception e) {
Log.e("exif", e.getMessage());
}
}
}