如何正确下采样图像? [英] How to downsample images correctly?

查看:191
本文介绍了如何正确下采样图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

创建一个应用程序,有很多高品质的图像,我决定为下限值的图像所需的大小(这意味着如果图像比屏幕大,我降尺度的话)。

Creating an app that has a lot of high quality images, I've decided to downscale the images to the needed size (meaning that if the image is larger than the screen , I downscale it ) .

我已经注意到,在某些设备上,如果图像被缩减,他们变得​​模糊/像素化,但在相同的设备,为同一个目标ImageView的大小,如果图像不缩小,他们蛮好看的。

I've noticed that on some devices, if the images are downscaled, they become blurry/pixelated, yet on the same devices, for the same target imageView size, if the images aren't downscaled, they look just fine.

我已经决定为了进一步确认这一问题,并建立了一个小的POC应用程序,显示了问题。

I've decided to check this issue further, and created a small POC app that shows the issue.

在向你展示code,这里有一个演示什么我说的:

Before showing you code, here's a demo of what I'm talking about :

这是一个有点难以看出差别,但你可以看到第二个是有点像素化。这可以显示在任何图像。

it's a bit hard to see the difference, but you can see that the second is a bit pixelated . this can be shown on any image.

public class MainActivity extends Activity
  {
  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ImageView originalImageView=(ImageView)findViewById(R.id.originalImageView);
    final ImageView halvedImageView=(ImageView)findViewById(R.id.halvedImageView);
    final ImageView halvedBitmapImageView=(ImageView)findViewById(R.id.halvedBitmapImageView);
    //
    final Bitmap originalBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.test);
    originalImageView.setImageBitmap(originalBitmap);
    halvedImageView.setImageBitmap(originalBitmap);
    //
    final LayoutParams layoutParams=halvedImageView.getLayoutParams();
    layoutParams.width=originalBitmap.getWidth()/2;
    layoutParams.height=originalBitmap.getHeight()/2;
    halvedImageView.setLayoutParams(layoutParams);
    //
    final Options options=new Options();
    options.inSampleSize=2;
    // options.inDither=true; //didn't help
    // options.inPreferQualityOverSpeed=true; //didn't help
    final Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.test,options);
    halvedBitmapImageView.setImageBitmap(bitmap);
    }
  }

XML:

<ScrollView 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" tools:context=".MainActivity"
  android:fillViewport="true">
  <HorizontalScrollView android:layout_width="match_parent"
    android:fillViewport="true" android:layout_height="match_parent">
    <LinearLayout android:layout_width="match_parent"
      android:layout_height="match_parent" android:orientation="vertical">


      <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="original" />

      <ImageView android:layout_width="wrap_content"
        android:id="@+id/originalImageView" android:layout_height="wrap_content" />

      <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="original , imageView size is halved" />

      <ImageView android:layout_width="wrap_content"
        android:id="@+id/halvedImageView" android:layout_height="wrap_content" />

      <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="bitmap size is halved" />

      <ImageView android:layout_width="wrap_content"
        android:id="@+id/halvedBitmapImageView" android:layout_height="wrap_content" />

    </LinearLayout>
  </HorizontalScrollView>
</ScrollView>

问题

为什么会出现这种问题?

The question

Why does it occur?

这两种方法都应该具有相同的结果,如来自相同来源样品和使用相同的因子。

Both methods should have the same result, as both sample from the same source and using the same factor.

我试着用下采样的方法来打,但没有任何帮助。

I've tried to play with the downsampling method, but nothing has helped.

使用强度气体(而不是inSampleSize)似乎修复它,但我不知道该怎么为它设置。我认为,对于外界的图像(从网上为例),我可以将其设置为屏幕像素密度乘以样本大小我想使用。

Using the inDensity (instead of inSampleSize) seems to fix it, but I'm not sure what to set for it . i think that for outside images (from the internet for example), i can set it to the screen density multiplied by the sample size i wish to use.

但是,它甚至一个很好的解决方案?我应该怎么做,在图像的资源文件夹内的情况下(我不认为这是一个函数来获取其密度文件夹中的位图位于)?为什么它在使用推荐的工作方式(谈到这里)无法正常工作好?

But is it even a good solution? what should i do in the case the images are inside the resources folder (i don't think there is a function to get which density folder a bitmap is located at) ? why does it work while using the recommended way (talked about here) doesn't work well ?

编辑:我发现了一个诀窍获得它的密度是用在这里为你从资源获取的可绘制(链接)。然而,这不是面向未来的,因为你需要具体的密度来检测

I've found out a trick to get which density is used for a drawable you get from the resources (link here) . however, it isn't future proof, as you need to be specific to the density to detect.

推荐答案

好了,我找到了一个不错的选择,我认为应该适用于任何类型的位图解码。

ok, i've found a nice alternative, which i think should work for any kind of bitmap decoding.

不仅如此,但它也可以让你使用任何你想要的样本量为下限值,而不是2只的能力。如果你把更多的精力,你甚至可以使用分数,而不是整数的按比例缩小。

not only that, but it also allows you to downscale using any sample size you wish, and not just the power of 2 . if you put more effort to it, you can even use fractions instead of integers for the downscaling.

下面作品的code代表从res文件夹的影像,但可以很容易地为任何种类的位图的解码完成的:

the code below works for images from the res folder, but it can easily be done for any kind of bitmap decoding:

private Bitmap downscaleBitmapUsingDensities(final int sampleSize,final int imageResId)
  {
  final Options bitmapOptions=new Options();
  bitmapOptions.inDensity=sampleSize;
  bitmapOptions.inTargetDensity=1;
  final Bitmap scaledBitmap=BitmapFactory.decodeResource(getResources(),imageResId,bitmapOptions);
  scaledBitmap.setDensity(Bitmap.DENSITY_NONE);
  return scaledBitmap;
  }

我测试过它,它显示了下采样图像就好了。下图中,我已经展示了原始图像,并利用德inSampleSize方法缩减图像,并使用我的方法。

i've tested it and it shows the downsampled images just fine. in the image below, i've shown the original image, and downscaling the image using teh inSampleSize method, and using my method.

这是很难看出差别,但使用的密度其实并不只是跳过像素,但使用所有这些的一个考虑。它可能是一个有点慢,但它更precise并使用更好的插值。

it's hard to see the difference, but the one that uses the density actually doesn't just skip pixels, but uses all of them to take into account. it might be a bit slower, but it's more precise and uses a nicer interpolation.

相比使用inSampleSize的唯一缺点似乎是速度,这是上inSampleSize更好,因为inSampleSize跳过像素并且由于密度方法确实对被跳过的象素额外的计算。

the only disadvantage compared to using the inSampleSize seems to be speed, which is better on inSampleSize because inSampleSize skips pixels and because the densities method does extra calculations on the skipped pixels.

不过,我觉得,不知怎的,Android的运行这两种方法在大约相同的速度。

However, i think that somehow android runs both methods in about the same speed.

我认为2种方法比较类似近邻下采样和双线性插值下采样

I think the 2 methods comparison is similar to the comparison between the nearest-neighbor downsampling and the bilinear-interpolation downsampling.

编辑:我发现我已经在这里所示的方法的一个缺点,相对于一个谷歌了。在此过程中使用的内存可以说是相当高的,而且我认为这取决于图像本身上。这意味着你应该使用它只有在情况下,你觉得是有意义的。

i've found one downside of the method i've shown here, compared to the one Google has . the memory used during the process can be quite high, and i think it depends on the image itself. this means you should use it only on cases you think make sense.

编辑:我已经为那些谁愿意克服了内存问题做了合并后的解决方案(包括谷歌的解决方案,我的)。它并不完美,但它比我以前做的更好,因为随着原始位下采样过程中需要它不会使用尽可能多的内存。相反,如谷歌的解决方案中使用它会使用内存。

i've made a merged solution (both google's solution and mine) for those who wish to overcome the memory problem. it's not perfect, but it's better than what i did before, because it won't use as much memory as the original bitmap needs during the downsampling. instead, it will use the memory as used in google's solution.

这里的code:

    // as much as possible, use google's way to downsample:
    bitmapOptions.inSampleSize = 1;
    bitmapOptions.inDensity = 1;
    bitmapOptions.inTargetDensity = 1;
    while (bitmapOptions.inSampleSize * 2 <= inSampleSize)
        bitmapOptions.inSampleSize *= 2;

    // if google's way to downsample isn't enough, do some more :
    if (bitmapOptions.inSampleSize != inSampleSize) 
      {
      // downsample by bitmapOptions.inSampleSize/originalSampleSize .
      bitmapOptions.inTargetDensity = bitmapOptions.inSampleSize;
      bitmapOptions.inDensity = inSampleSize;
      } 
    else if(sampleSize==1)
      {
      bitmapOptions.inTargetDensity=preferHeight ? reqHeight : reqWidth;
      bitmapOptions.inDensity=preferHeight ? height : width;
      }

等等,总之,这两种方法的优点和缺点:

so, in short, the pros and cons of both methods:

谷歌的方式(使用inSampleSize)解码过程中使用较少的内存,并且速度更快。 但是,它会导致有时某些图形文物,它仅支持下采样,以2的幂,所以结果的位图可能会超过你想要的(对X1 / 4,而不是X1 / 7的例子大小)。

Google's way (using inSampleSize) uses less memory during decoding, and is faster. However, it causes some graphical artifacts sometimes and it only supports downsampling to the power of 2, so the result bitmap might take more than what you wanted (for example size of x1/4 instead of x1/7) .

我的方式(使用密度)更precise,给出更高质量的图像,并在结果位图使用更少的内存。 但是,它可以在解码过程中使用了大量的内存(取决于输入),这是一个有点慢。

My way (using densities) is more precise, gives higher quality images, and uses less memory on the result bitmap. However, it can use a lot of memory during the decoding (depends on the input) and it's a bit slower.

编辑:另一种改进,因为我发现,在某些情况下,输出图像不符合要求的尺寸限制,你不希望下采样过多使用谷歌的方式:

another improvement, as I've found that on some cases the output image doesn't match the required size restriction, and you don't wish to downsample too much using Google's way :

    final int newWidth = width / bitmapOptions.inSampleSize, newHeight = height / bitmapOptions.inSampleSize;
    if (newWidth > reqWidth || newHeight > reqHeight) {
        if (newWidth * reqHeight > newHeight * reqWidth) {
            // prefer width, as the width ratio is larger
            bitmapOptions.inTargetDensity = reqWidth;
            bitmapOptions.inDensity = newWidth;
        } else {
            // prefer height
            bitmapOptions.inTargetDensity = reqHeight;
            bitmapOptions.inDensity = newHeight;
        }
    }

因此​​,例如,从2448x3264图像下采样至1200x1200,它将成为900x1200

So, for example, downsampling from 2448x3264 image to 1200x1200, it will become 900x1200

这篇关于如何正确下采样图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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