Android:使用比双线性更好的重采样algorithm进行位图大小调整(如Lanczos3)

是否有任何方式或外部图书馆,可以调整图像使用Lanczos (理想)或至less双三次阿尔格调整图像。 在Android下? ( 当然速度越快越好,但质量优先,处理时间次要)

我到目前为止所做的一切是这样的:

Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true); 

但是它使用双线性滤波器,输出质量很差。 特别是如果你想保存细节(如细线或可读文本)。

Java有许多好的库,如下面所讨论的: Java – 调整图像的大小而不损失质量

然而,它总是依赖于java.awt.image.BufferedImage类的Java awt类,所以它不能在Android中使用。

有没有办法如何改变Bitmap.createScaledBitmap()方法中的默认(双线性)filter或像Morten Nobel的能够与android.graphics.Bitmap类一起工作的库 (或一些原始表示forms,如@Tron评论指出)?

Solutions Collecting From Web of "Android:使用比双线性更好的重采样algorithm进行位图大小调整(如Lanczos3)"

最有希望的IMO是使用libswscale (来自FFmpeg),它提供了Lanczos和其他许多filter。 要从本地代码访问Bitmap缓冲区,您可以使用jnigraphics 。 这种方法保证了良好的性能和可靠的结果

编辑

在这里你可以find粗糙的演示应用程序,使用提出的方法。 目前的performance令人沮丧,所以应该调查一下,以决定是否做一些改进。

不幸的是,Android使用android中不存在的android.graphics.Bitmap,而java使用java.awt.image.BufferedImage,它不存在于android 🙁

我没有a ready to use library for android但一个path如何将一个java-awt特定库移植到一个平台独立Java库与平台的android和awt / j2se特定处理程序

在java的rescale库中,你必须隐藏所有的java-awt特定类(比如BufferedImage)在一个接口IBitmap后面,并为j2se和独立的Android实现这个接口。

我为exif / icc / ipc元数据处理成功完成了这个工作,并实现了piximeta-lib /…/IBitmap.java , Android的LIB /…/的Android / BitmapNative.java

所以我有这些包

  • pixymeta-LIB
    • 转换后的平台独立库,所有的awt参考文件都被IBitmap接口replace
  • pixymeta-J2SE-LIB
    • awt / j2se实现IBitmap
  • pixymeta-Android的LIB
    • android实现的IBitmap

我最近写了这个缩放/裁剪一个图像到一个特定的分辨率和质量压缩:

 public static void scaleImageToResolution(Context context, File image, int dstWidth, int dstHeight) { if (dstHeight > 0 && dstWidth > 0 && image != null) { Bitmap result = null; try { //Get Image Properties BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(image.getAbsolutePath(), bmOptions); int photoH = bmOptions.outHeight; int photoW = bmOptions.outWidth; bmOptions.inJustDecodeBounds = false; bmOptions.inPurgeable = true; //Smaller Image Size in Memory with Config bmOptions.inPreferredConfig = Bitmap.Config.RGB_565; //Is resolution not the same like 16:9 == 4:3 then crop otherwise fit ScalingLogic scalingLogic = getScalingLogic(photoW, photoH,dstWidth, dstHeight); //Get Maximum automatic downscaling that it's still bigger then this requested resolution bmOptions.inSampleSize = calculateScalingSampleSize(photoW, photoH, dstWidth, dstHeight, scalingLogic); //Get unscaled Bitmap result = BitmapFactory.decodeFile(image.getAbsolutePath(), bmOptions); //Scale Bitmap to requested Resolution result = scaleImageToResolution(context, result, scalingLogic); if (result != null) { //Save Bitmap with quality saveImageWithQuality(context, result, image); } } finally { //Clear Memory if (result != null) result.recycle(); } } } public static void saveImageWithQuality(Bitmap bitmap, String path, int compressQuality) { try { FileOutputStream fOut; fOut = new FileOutputStream(path); bitmap.compress(Bitmap.CompressFormat.JPEG, compressQuality, fOut); fOut.flush(); fOut.close(); } catch (IOException ex) { if (Logger.getRootLogger() != null) Logger.getRootLogger().error(ex); else Log.e("saveImageWithQuality", "Error while saving compressed Picture: " + ex.getMessage() + StringUtils.newLine() + ex.getStackTrace().toString()); } } public static void saveImageWithQuality(Context context, Bitmap bitmap, File file) { saveImageWithQuality(bitmap, file.getAbsolutePath(), getCompressQuality()); } public static void saveImageWithQuality(Context context, Bitmap bitmap, String path) { saveImageWithQuality(bitmap, path, getCompressQuality()); } private static int calculateScalingSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.FIT) { final float srcAspect = (float) srcWidth / (float) srcHeight; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect > dstAspect) { return srcWidth / dstWidth; } else { return srcHeight / dstHeight; } } else { final float srcAspect = (float) srcWidth / (float) srcHeight; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect > dstAspect) { return srcHeight / dstHeight; } else { return srcWidth / dstWidth; } } } private static Bitmap scaleImageToResolution(Context context, Bitmap unscaledBitmap, ScalingLogic scalingLogic, int dstWidth, int dstHeight) { //Do Rectangle of original picture when crop Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic); //Do Rectangle to fit in the source rectangle Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic); //insert source rectangle into new one Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(scaledBitmap); canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG)); //Recycle the unscaled Bitmap afterwards unscaledBitmap.recycle(); return scaledBitmap; } private static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.CROP) { if (srcWidth >= srcHeight) { //Horizontal final float srcAspect = (float) srcWidth / (float) srcHeight; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect < dstAspect || isResolutionEqual(srcAspect, dstAspect)) { final int srcRectHeight = (int) (srcWidth / dstAspect); final int scrRectTop = (srcHeight - srcRectHeight) / 2; return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight); } else { final int srcRectWidth = (int) (srcHeight * dstAspect); final int srcRectLeft = (srcWidth - srcRectWidth) / 2; return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight); } } else { //Vertikal final float srcAspect = (float) srcHeight / (float) srcWidth; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect < dstAspect || isResolutionEqual(srcAspect, dstAspect)) { final int srcRectWidth = (int) (srcHeight / dstAspect); final int srcRectLeft = (srcWidth - srcRectWidth) / 2; return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight); } else { final int srcRectHeight = (int) (srcWidth * dstAspect); final int scrRectTop = (srcHeight - srcRectHeight) / 2; return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight); } } } else { return new Rect(0, 0, srcWidth, srcHeight); } } private static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.FIT) { if (srcWidth > srcHeight) { //Vertikal final float srcAspect = (float) srcWidth / (float) srcHeight; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect < dstAspect || isResolutionEqual(srcAspect, dstAspect)) { return new Rect(0, 0, (int) (dstHeight * srcAspect), dstHeight); } else { return new Rect(0, 0, dstWidth, (int) (dstWidth / srcAspect)); } } else { //Horizontal final float srcAspect = (float) srcHeight / (float) srcWidth; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect < dstAspect || isResolutionEqual(srcAspect, dstAspect)) { return new Rect(0, 0, (int) (dstHeight / srcAspect), dstHeight); } else { return new Rect(0, 0, dstWidth, (int) (dstWidth * srcAspect)); } } } else { if (srcWidth >= srcHeight) return new Rect(0, 0, dstWidth, dstHeight); else return new Rect(0, 0, dstHeight, dstWidth); } } private static ScalingLogic getScalingLogic(int imageWidth, int imageHeight, int dstResolutionWidth, int dstResolutionHeight) { if (imageWidth >= imageHeight) { //Bild horizontal final float srcAspect = (float) imageWidth / (float) imageHeight; final float dstAspect = (float) dstResolutionWidth / (float) dstResolutionHeight; if (!isResolutionEqual(srcAspect, dstAspect)) { return ScalingLogic.CROP; } else { return ScalingLogic.FIT; } } else { //Bild vertikal final float srcAspect = (float) imageHeight / (float) imageWidth; final float dstAspect = (float) dstResolutionWidth / (float) dstResolutionHeight; if (!isResolutionEqual(srcAspect, dstAspect)) { return ScalingLogic.CROP; } else { return ScalingLogic.FIT; } } } public enum PictureQuality { High, Medium, Low } public enum ScalingLogic { CROP, FIT } //Does resolution match private static boolean isResolutionEqual(float v1, float v2) { // Falls a 1.999999999999 and b = 2.000000000000 return v1 == v2 || Math.abs(v1 - v2) / Math.max(Math.abs(v1), Math.abs(v2)) < 0.01; } public int getCompressQuality() { if (Quality == PictureQuality.High) return 100; else if (Quality == PictureQuality.Medium) return 50; else if (Quality == PictureQuality.Low) return 25; else return 0; } 

它没有使用你提到的库,但它的工作,我很高兴。 也许你也是。

这里是我用来调整图像大小的代码

 Bitmap photo1 ; private byte[] imageByteArray1 ; BitmapFactory.Options opt1 = new BitmapFactory.Options(); opt1.inJustDecodeBounds=true; BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),opt1); // The new size we want to scale to final int REQUIRED_SIZE=320; // Find the correct scale value. It should be the power of 2. int width_tmp=opt1.outWidth,height_tmp=opt1.outHeight; int scale=2; while(true){ if(width_tmp>REQUIRED_SIZE||height_tmp>REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; scale*=2; } // Decode with inSampleSize BitmapFactory.Options o2=new BitmapFactory.Options(); o2.inSampleSize=scale; o2.inJustDecodeBounds=false; photo1=BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),o2); ByteArrayOutputStream baos1=new ByteArrayOutputStream(); photo1.compress(Bitmap.CompressFormat.JPEG,60,baos1); imageByteArray1=baos1.toByteArray(); 

希望它会帮助你..

如果你只是想以一种为了显示而优化的方式来重新采样图像,那么你可以使用这个漂亮的小内胆来为我服务。

 Bitmap bitmap = new BitmapDrawable(getResources(), yourBitmap).getBitmap(); 

这行代码可能看起来很奇怪,因为您将位图转换为BitmapDrawable并再次转换为位图,但BitmapDrawable默认为设备的像素密度(除非使用其他构造函数)。

如果您还需要resize,那么只需将它分成两行,然后在将BitmapDrawable转换回位图之前使用setBounds,如下所示:

 BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), yourBitmap); bitmapDrawable.setBounds(left, top, right, bottom); //Make it a new size in pixels. yourBitmap = bitmapDrawable.getBitmap(); //Convert it back to a bitmap optimised for display purposes. 

可绘制的位图可能被列为已删除,但不是,只有某些构造函数被删除,并且上述示例中的构造函数未被删除。 这也将与API 4一起工作

另外android文档有一个可下载的样本在这里: https : //developer.android.com/topic/performance/graphics/load-bitmap.html

希望这可以帮助。