安卓:高质量图像调整/缩放

27

我需要缩小从网络流中获取的图像而不会失去质量。

我知道这个解决方案(在将图像加载到位图对象时出现奇怪的内存问题),但它太粗糙了--inSampleSize是一个整数,不能对结果尺寸进行更细致的控制。即,我需要将图像缩放到特定的h/w尺寸(并保持长宽比)。

我不介意在我的代码中使用DIY双三次/兰卡兹算法,但我找不到任何可在android上工作的示例,因为它们都依赖于Java2D(JavaSE)。

编辑: 我附上了一个快速源码。原始的是720x402高清屏幕截图。请忽略前两个缩略图。顶部大图像由Android自动调整大小(作为布局的一部分)约为130x72。它很好看。底部的图像使用API调整大小,具有严重的伪影。

alt text

我还尝试过使用BitmapFactory,就像我之前说过的,它有两个问题--没有办法缩放到精确的大小,而且缩放后的图像模糊。

有什么想法可以解决伪影问题吗?

谢谢,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);
    }

}

而且布局

<?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>

我认为顶部的图片实际上不是130像素宽。http://icons-search.com/img/fasticon/fast_emoticons_lnx.zip/fast_emoticons_lnx-Icons-128X128-smile_1.png-128x128.png 宽度为128像素... - Quamis
请查看关于解码图像的这个教程 - android developer
嗨!遇到了同样的问题,你找到解决方案了吗? - Dehimb
3个回答

9
您可以使用 BitmapFactory.Options 结合 BitmapFactory.decode 函数,
使用 inDensityinTargetDensity 参数
例如:您有一张尺寸为1600x1200的图像,想要将其调整为640x480
那么设置 'inDensity'=5'inTargetDensity'=2 (1600x2等于640x5)。
希望这能对您有所帮助。

不幸的是,这会产生最近邻居下采样。正如我所发现的那样,为了保持双三次下采样,只允许更改inSampleSize。 - goRGon

8
顶部的大图像只是按照布局缩小到450px,因此没有伪影。
底部大图像的伪影是由于将其缩小到130px宽,然后再通过布局放大到约450px而导致的。因此,伪影是由您的缩放造成的。请尝试更改缩放方式。
Bitmap resized = getResizedBitmap(original, 450);

在您的代码中加入这段代码就可以了。但是,您需要根据手机的实际屏幕宽度进行适配。


4
简而言之,好的缩小算法(不是最近邻算法)包括两个步骤:
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-module/image-scaling-code-example-for-android/

不幸的是,那些链接已经无法使用了。 - Hugo Gresse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接