在运行时调整图像大小时出现质量问题 [英] Quality problems when resizing an image at runtime

查看:22
本文介绍了在运行时调整图像大小时出现质量问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在磁盘上有一个图像文件,我正在调整文件大小并将其作为新图像文件保存回磁盘.为了这个问题,我不是为了在屏幕上显示它们而将它们带入内存,只是为了调整它们的大小并重新保存它们.这一切都很好.但是,缩放后的图像上有伪像,如下所示:android: quality在运行时调整大小的图像

I have a image file on the disk and I am resizing the file and saving it back to disk as a new image file. For the sake of this question, I am not bringing them into memory in order to display them on the screen, only to resize them and resave them. This all works just fine. However, the scaled images have artifacts on them like shown here: android: quality of the images resized in runtime

它们以这种失真保存,因为我可以将它们从磁盘中取出并在我的计算机上查看它们,但它们仍然存在相同的问题.

They are saved with this distortion, as I can pull them off the disk and look at them on my computer and they still have the same issue.

我正在使用类似于此奇怪的内存不足问题的代码将图像加载到位图对象以将位图解码到内存中:

I am using code similar to this Strange out of memory issue while loading an image to a Bitmap object to decode the bitmap into memory:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageFilePathString, options);

int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
int scale = 1;

while(srcWidth / 2 > desiredWidth){
   srcWidth /= 2;
   srcHeight /= 2;
   scale *= 2;
}

options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = scale;
Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(imageFilePathString, options);

然后我正在做实际的缩放:

Then I am doing the actual scaling with:

Bitmap scaledBitmap = Bitmap.createScaledBitmap(sampledSrcBitmap, desiredWidth, desiredHeight, false);

最后,新调整大小的图像保存到磁盘:

Lastly, the new resized image is saved to disk with:

FileOutputStream out = new FileOutputStream(newFilePathString);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);

然后,正如我所提到的,如果我从磁盘中取出该文件并查看它,它具有上面链接的质量问题并且看起来很糟糕.如果我跳过 createScaledBitmap 并将 sampledSrcBitmap 直接保存回磁盘,则没有问题,这似乎只有在大小发生变化时才会发生.

Then, as I mentioned, if I pull that file off the disk and look at it, it has that quality issue linked above and looks terrible. If I skip the createScaledBitmap and just save the sampledSrcBitmap right back to disk there is no problem, it seems to only happen if the size changes.

我已经尝试过,正如您在代码中看到的那样,将 inDither 设置为 false,如此处所述http://groups.google.com/group/android-developers/browse_thread/thread/8b1abdbe881f9f71 以及上面第一个链接帖子中提到的.那没有改变任何东西.另外,在我链接的第一篇文章中,Romain Guy 说:

I have tried, as you can see in the code, setting inDither to false as mentioned here http://groups.google.com/group/android-developers/browse_thread/thread/8b1abdbe881f9f71 and as mentioned in the very first linked post above. That didn't change anything. Also, in the first post I linked, Romain Guy said:

而不是在绘图时调整大小(这将是非常昂贵的),尝试在屏幕外位图中调整大小并确保位图是 32 位(ARGB888).

Instead of resizing at drawing time (which is going to be very costly), try to resize in an offscreen bitmap and make sure that Bitmap is 32 bits (ARGB888).

但是,我不知道如何确保位图在整个过程中保持为 32 位.

However, I have no idea how to make sure the Bitmap stays as 32 bits through the whole process.

我还阅读了其他几篇文章,例如http://android.nakatome.net/2010/04/bitmap-basics.html 但他们似乎都在解决绘制和显示位图的问题,我只是想调整它的大小并将其保存回磁盘而没有这个质量问题.

I have also read a couple other articles such as this http://android.nakatome.net/2010/04/bitmap-basics.html but they all seemed to be addressing drawing and displaying the Bitmap, I just want to resize it and save it back to disk without this quality problem.

非常感谢

推荐答案

经过反复试验,我终于找到了一种方法来做到这一点,而且效果很好.我会为将来可能发现此答案有帮助的任何人写这篇文章.

After experimenting I have finally found a way to do this with good quality results. I'll write this up for anyone that might find this answer helpful in the future.

要解决第一个问题,即图像中引入的伪像和奇怪的抖动,您需要确保图像保持为 32 位 ARGB_8888 图像.使用我的问题中的代码,您可以在第二次解码之前将此行添加到选项中.

To solve the first problem, the artifacts and weird dithering introduced into the images, you need to insure your image stays as a 32-bit ARGB_8888 image. Using the code in my question, you can simply add this line to the options before the second decode.

options.inPreferredConfig = Bitmap.Config.ARGB_8888;

添加后,伪影消失了,但整个图像的边缘变得锯齿状而不是清晰.经过更多实验后,我发现使用 Matrix 而不是 Bitmap.createScaledBitmap 调整位图大小会产生更清晰的结果.

After adding that, the artifacts were gone but edges throughout the images came through jagged instead of crisp. After some more experimentation I discovered that resizing the bitmap using a Matrix instead of Bitmap.createScaledBitmap produced much crisper results.

通过这两种解决方案,图像现在可以完美地调整大小.下面是我使用的代码,以防其他人遇到这个问题.

With those two solutions, the images are now resizing perfectly. Below is the code I am using in case it benefits someone else coming across this problem.

// Get the source image's dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(STRING_PATH_TO_FILE, options);

int srcWidth = options.outWidth;
int srcHeight = options.outHeight;

// Only scale if the source is big enough. This code is just trying to fit a image into a certain width.
if(desiredWidth > srcWidth)
    desiredWidth = srcWidth;



// Calculate the correct inSampleSize/scale value. This helps reduce memory use. It should be a power of 2
// from: https://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/823966#823966
int inSampleSize = 1;
while(srcWidth / 2 > desiredWidth){
    srcWidth /= 2;
    srcHeight /= 2;
    inSampleSize *= 2;
}

float desiredScale = (float) desiredWidth / srcWidth;

// Decode with inSampleSize
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = inSampleSize;
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(STRING_PATH_TO_FILE, options);

// Resize
Matrix matrix = new Matrix();
matrix.postScale(desiredScale, desiredScale);
Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0, sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
sampledSrcBitmap = null;

// Save
FileOutputStream out = new FileOutputStream(NEW_FILE_PATH);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
scaledBitmap = null;

在对此进行持续工作后,我发现图像仍然不是 100% 完美.如果可以改进,我会更新.

After continual work on this I have found that the images still aren't 100% perfect. I'll make an update if I can improve it.

更新: 修改后,我发现 关于 SO 的这个问题,有一个答案提到了 inScaled 选项.这也有助于提高质量,所以我添加了更新上面的答案以包含它.我现在也将位图使用完毕后清零.

Update: After revisting this, I found this question on SO and there was an answer that mentioned the inScaled option. This helped with the quality as well so I added updated the answer above to include it. I also now null the bitmaps after they are done being used.

另外,作为旁注,如果您在 WebView 中使用这些图像,请确保使用 考虑这篇文章.

Also, as a side note, if you are using these images in a WebView, make sure you take this post into consideration.

注意:您还应该添加一个检查以确保宽度和高度是有效数字(不是 -1).如果是,则会导致 inSampleSize 循环变得无限.

Note: you should also add a check to make sure the width and height are valid numbers (not -1). If they are, it will cause the inSampleSize loop to become infinite.

这篇关于在运行时调整图像大小时出现质量问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆