Android:高质量图像调整大小/缩放

发布于 2024-10-02 19:23:10 字数 4198 浏览 10 评论 0原文

我需要缩小来自网络流的图像而不损失质量。

我知道这个解决方案 将图像加载到位图对象时出现奇怪的内存不足问题< /a> 但它太粗略 - inSampleSize 是一个整数,不允许对结果尺寸进行更精细的控制。也就是说,我需要将图像缩放到特定的高/宽尺寸(并保持纵横比)。

我不介意在我的代码中使用 DIY 双三次/lancoz 算法,但我找不到任何适用于 Android 的示例,因为它们都依赖于 Java2D (JavaSE)。

编辑: 我附上了一个快速来源。原图是720x402高清屏幕截图。请忽略最上面的 2 个缩略图。顶部大图像由 android 自动调整大小(作为布局的一部分)至约 130x72。它又漂亮又清脆。底部图像使用 API 调整大小,并且有严重的伪像

alt text

我也尝试过使用 BitmapFactory,正如我所说早些时候,它有两个问题——无法缩放到精确大小,并且缩放后的图像模糊。

关于如何解决修复问题有什么想法吗?

谢谢,所以!

    package qp.test;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;

public class imgview extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
        Bitmap resized = getResizedBitmap(original, 130);
        //Bitmap resized = getResizedBitmap2(original, 0.3f);
        System.err.println(resized.getWidth() + "x" + resized.getHeight());

        ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
        image.setImageBitmap(resized);

    }

    private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {

        int width = bm.getWidth();

        int height = bm.getHeight();

    float aspect = (float)width / height;

    float scaleWidth = newWidth;

    float scaleHeight = scaleWidth / aspect;        // yeah!

    // create a matrix for the manipulation

    Matrix matrix = new Matrix();

    // resize the bit map

    matrix.postScale(scaleWidth / width, scaleHeight / height);

    // recreate the new Bitmap

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        bm.recycle();

        return resizedBitmap;
    }

    private Bitmap getResizedBitmap2(Bitmap bm, float scale) {

    /*    float aspect = bm.getWidth() / bm.getHeight();

        int scaleWidth = (int) (bm.getWidth() * scale);
        int scaleHeight = (int) (bm.getHeight() * scale);
*/
        // original image is 720x402 and SampleSize=4 produces 180x102, which is
        // still too large

        BitmapFactory.Options bfo = new BitmapFactory.Options();
        bfo.inSampleSize = 4;

        return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
    }

}

以及布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
<!-- <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="hullo" android:background="#00ff00"
    />
     -->
    <ImageView android:id="@+id/ImageViewThumbAuto"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  />

    <ImageView android:id="@+id/ImageViewThumbManual"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  
            android:layout_toRightOf="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullAuto" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullManual" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewFullAuto"
            />

</RelativeLayout>

I need to scale down images coming from a Network stream without losing quality.

I am aware of this solution Strange out of memory issue while loading an image to a Bitmap object but it is too coarse - inSampleSize is an integer and does not allow finer control over the resulting dimensions. That is, I need to scale images to specific h/w dimensions (and keeping aspect ratio).

I dont mind having a DIY bicubic/lancoz algorithm in my code but I cant find any examples that would work on Android as they all rely on Java2D (JavaSE).

EDIT:
Ive attached a quick source. The original is 720x402 HD screen capture. Please ignore the top 2 thumbnails. The top large image is resized automatically by android (as part of layout) to about 130x72. It is nice and crisp. The bottom image is resized with API and has severe artifacting

alt text

I've also tried using the BitmapFactory and, as I said earlier, it has two problems - no way to scale to exact size and the scaled image is blurry.

Any ideas on how to fix the artifcating?

Thanks, S.O.!

    package qp.test;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;

public class imgview extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
        Bitmap resized = getResizedBitmap(original, 130);
        //Bitmap resized = getResizedBitmap2(original, 0.3f);
        System.err.println(resized.getWidth() + "x" + resized.getHeight());

        ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
        image.setImageBitmap(resized);

    }

    private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {

        int width = bm.getWidth();

        int height = bm.getHeight();

    float aspect = (float)width / height;

    float scaleWidth = newWidth;

    float scaleHeight = scaleWidth / aspect;        // yeah!

    // create a matrix for the manipulation

    Matrix matrix = new Matrix();

    // resize the bit map

    matrix.postScale(scaleWidth / width, scaleHeight / height);

    // recreate the new Bitmap

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        bm.recycle();

        return resizedBitmap;
    }

    private Bitmap getResizedBitmap2(Bitmap bm, float scale) {

    /*    float aspect = bm.getWidth() / bm.getHeight();

        int scaleWidth = (int) (bm.getWidth() * scale);
        int scaleHeight = (int) (bm.getHeight() * scale);
*/
        // original image is 720x402 and SampleSize=4 produces 180x102, which is
        // still too large

        BitmapFactory.Options bfo = new BitmapFactory.Options();
        bfo.inSampleSize = 4;

        return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
    }

}

And the layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
<!-- <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="hullo" android:background="#00ff00"
    />
     -->
    <ImageView android:id="@+id/ImageViewThumbAuto"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  />

    <ImageView android:id="@+id/ImageViewThumbManual"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  
            android:layout_toRightOf="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullAuto" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullManual" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewFullAuto"
            />

</RelativeLayout>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

[旋木] 2024-10-09 19:23:10

您可以将 BitmapFactory.OptionsBitmapFactory.decode 函数一起使用,
使用 inDensityinTargetDensity

示例:您的图像尺寸为 1600x1200并希望将大小调整为 640x480
然后 'inDensity'=5'inTargetDensity'=2(1600x2 等于 640x5)。

希望有所帮助。

You can use BitmapFactory.Options with BitmapFactory.decode function(s),

using inDensity and inTargetDensity

Example: you have 1600x1200 size of image and want to resize to 640x480

then 'inDensity'=5 and 'inTargetDensity'=2 (1600x2 equal to 640x5).

Hoping this help.

李白 2024-10-09 19:23:10

顶部的大图像只是通过布局缩小到 450 像素,因此没有任何伪影。

底部大图像的伪像是由于布局将其缩小到 130 像素宽,然后再次放大到约 450 像素而产生的。所以这些工件是由你的缩放造成的。尝试

Bitmap resized = getResizedBitmap(original, 450);

一下你的代码,应该没问题。但是,您也需要将其调整为手机的实际屏幕宽度。

The top large image is simply scaled down to 450px by the layout so no artifacts.

The artifacts of the bottom large image result from scaling it down to 130px wide and then up again to about 450px by the layout. So the artifacts are made by your scaling. Try

Bitmap resized = getResizedBitmap(original, 450);

in your code and it should be fine. However, you need to adapt that to the actuall screen width of the phone either.

情独悲 2024-10-09 19:23:10

简而言之,好的缩小算法(不像最近邻算法)由 2 个步骤组成:

  1. 使用 BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() 尽可能接近地缩小规模到您需要的分辨率,但不低于它
  2. 通过使用 Canvas::drawBitmap() 稍微缩小一点来达到精确的分辨率,

这里是 SonyMobile 如何解决这个问题的详细说明任务: http:// developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/

这是 SonyMobile 缩放实用程序的源代码: http://developer.sonymobile.com/downloads/code-example-模块/image-scaling-code-example-for-android/

Briefly, good downscaling algorithm (not nearest neighbor like) consists of 2 steps:

  1. downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
  2. get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()

Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/

Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文