Androidで大きなビットマップファイルのサイズを変更して出力ファイルをスケーリングする


218

ファイルに大きなビットマップ(3888x2592など)があります。次に、そのビットマップのサイズを800x533に変更して、別のファイルに保存します。通常、Bitmap.createBitmapメソッドを呼び出してビットマップをスケーリングしますが、最初の引数としてソースビットマップが必要です。元の画像をBitmapオブジェクトに読み込むとメモリが不足するため、これを指定することはできません(たとえば、こちらを参照)。

また、ビットマップを正確な幅と高さにサイズ変更したいのでBitmapFactory.decodeFile(file, options)、たとえばを指定してビットマップを読み取ることもできませんBitmapFactory.Options.inSampleSize。を使用inSampleSizeすると、ビットマップのサイズが972x648(使用する場合inSampleSize=4)または778x518(使用する場合inSampleSize=5、2の累乗すらありません)にサイズ変更されます。

また、最初のステップで972x648などのinSampleSizeを使用して画像を読み取り、2番目のステップで正確に800x533にサイズ変更することも避けたいです。

私の質問を要約すると、10MP以上の大きな画像ファイルを読み取り、それを新しい画像ファイルに保存し、OutOfMemory例外を発生させずに特定の新しい幅と高さにサイズ変更する方法はありますか?

またBitmapFactory.decodeFile(file, options)、Options.outHeightとOptions.outWidthの値を手動で800と533に設定してみましたが、そのようには動作しません。


いいえ、outHeightとoutWidthは、decodeメソッドからの出力パラメーターです。そうは言っても、私はあなたと同じ問題を抱えており、2ステップのアプローチには今まであまり満足していません。
RDS

多くの場合、ありがとうございます。1行のコードを使用できます.. stackoverflow.com/a/17733530/294884
Fattie

読者の方は、この非常に重要なQAにご注意ください!!! stackoverflow.com/a/24135522/294884
Fattie

1
Plsは、この質問は現在5年前のものであり、完全なソリューションは.. stackoverflow.com/a/24135522/294884です!
Fattie、2014

2
そのトピックに関する公式ドキュメントが用意されました:developer.android.com/training/displaying-bitmaps/...
ビンス・

回答:


146

いいえ。 誰かに修正してもらいたいのですが、妥協策として、ロード/サイズ変更のアプローチを受け入れました。

ブラウジングの手順は次のとおりです。

  1. inSampleSize目標よりも大きな画像を生成できる最大値を計算します。
  2. BitmapFactory.decodeFile(file, options)オプションとしてinSampleSizeを渡して、を使用して画像をロードします。
  3. を使用して、目的のサイズにサイズ変更しBitmap.createScaledBitmap()ます。

私はそれを避けようとしました。だから、たった1つのステップで大きな画像のサイズを直接変更する方法はありませんか?
マヌエル

2
私の知る限りではありませんが、それであなたがこれをさらに探求するのを止めないでください。
ジャスティン

承知しました。これまでのところ、これを受け入れられた回答と考えます。他の方法を見つけたらお知らせします。
マヌエル、

PSIXOが回答で述べたように、inSampleSize を使用しても問題が解決しない場合は、android:largeHeapを使用することできます。
user276648 2015年

ビットマップ変数は空になりました
Prasad

99

コードに翻訳されたジャスティンの回答(私にとっては完璧に機能します):

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();



    int scale = 1;
    while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", 
       orig-height: " + options.outHeight);

    Bitmap resultBitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        options = new BitmapFactory.Options();
        options.inSampleSize = scale;
        resultBitmap = BitmapFactory.decodeStream(in, null, options);

        // resize to desired dimensions
        int height = resultBitmap.getHeight();
        int width = resultBitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, 
           (int) y, true);
        resultBitmap.recycle();
        resultBitmap = scaledBitmap;

        System.gc();
    } else {
        resultBitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
       resultBitmap.getHeight());
    return resultBitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

15
"b"のような変数を使用すると読みにくくなりますが、それ以外の場合は適切な答えは得られません。
Oliver Dixon

@Ofir:getImageUri(path); このメソッドで何を渡さなければなりませんか?
Biginner 2013年

1
(w h)/Math.pow(scale、2)の代わりに、(w h)>>スケールを使用する方が効率的です。
david.perez 2014

2
電話しないでSystem.gc()ください
gw0 2015

@Ofirに感謝しますが、この変換では画像の向きが維持されません:-/
JoeCoolman

43

これは、「Mojo Risin」と「Ofir」のソリューションを「組み合わせた」ものです。これにより、最大の幅と最大の高さの境界で、比例してサイズ変更された画像が得られます。

  1. メタデータを読み取って元のサイズを取得するだけです(options.inJustDecodeBounds)
  2. メモリを節約するために粗いサイズ変更を使用します(itmap.createScaledBitmap)
  3. 以前に作成された大まかなBitampに基づいて正確にサイズ変更された画像を使用します。

私にとっては、以下の5つのMegaPixel画像でうまく機能しています。

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

    // decode image size (decode metadata only, not the whole image)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();
    in = null;

    // save width and height
    inWidth = options.outWidth;
    inHeight = options.outHeight;

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // resize bitmap
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}

23

APIを使用しないのはなぜですか?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);

21
それは私の問題を解決しないからです。「...最初の引数としてソースビットマップが必要ですが、元の画像をビットマップオブジェクトに読み込むとメモリが不足するため、これを指定することはできません。」したがって、ビットマップを.createScaledBitmapメソッドに渡すこともできません。これは、最初に大きな画像をビットマップオブジェクトにロードする必要があるためです。
Manuel

2
正しい。私はあなたの質問を読み直し、基本的に(正しく理解していれば)「元のファイルをメモリに読み込まずに画像を正確なサイズに変更できますか?」もしそうなら-私はそれに答えるための画像処理の複雑さについて十分に知りませんが、何かが私に1はAPIから利用できない、2は1ライナーではないということを教えてくれます。これをお気に入りとしてマークします-あなた(または他の誰か)がこれを解決するかどうかを確認するのは興味深いでしょう。
Bostone

私はuriを取得してビットマップに変換しているのでうまくいきました。それらのスケーリングは私にとっては最も簡単です。
Hamza

22

これまでに他の優れた答えを認めて、これについて私がまだ見た中で最高のコードは、写真撮影ツールのドキュメントにあります。

「スケーリングされたイメージのデコード」というタイトルのセクションを参照してください。

http://developer.android.com/training/camera/photobasics.html

それが提案するソリューションは、ここで他のソリューションと同様にサイズ変更してからスケーリングするソリューションですが、それは非常にきちんとしています。

便宜上、すぐに使える関数として以下のコードをコピーしました。

private void setPic(String imagePath, ImageView destination) {
    int targetW = destination.getWidth();
    int targetH = destination.getHeight();
    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    destination.setImageBitmap(bitmap);
}

1
最初に、結果をフロアする整数を分割します。次に、targetWまたはtargetHが0のときにコードがクラッシュします(これは私にはあまり意味がありません)。第三inSampleSizeは2の累乗でなければなりません
cybergen

誤解しないでください。これは画像を確実にロードしますが、intのフローリングが意図されている場合、そのようには見えません。また、画像が期待どおりに拡大縮小されないため、これも間違いなく正しい答えではありません。画像ビューが画像の半分以下のサイズになるまで何もしません。その後、画像ビューが画像のサイズの1/4になるまで何も起こりません。2のべき乗で続きます!
cybergen 2016年

18

これらの回答とAndroidのドキュメントを読んだ後、ビットマップのサイズをメモリに読み込まずにサイズ変更するコードを次に示します。

public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    //inJustDecodeBounds = true <-- will not load the bitmap into memory
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    return(bitmap);
}

bmOptions.inPurgeable = trueであることに注意してください。廃止予定です。
Ravit

6

大きなビットマップがあり、サイズを変更してデコードしたい場合は、次のようにします

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);

1
inSampleSizeはIntegerであるため、取得する正確なピクセルの幅と高さを取得することはほとんどありません。小数によっては、接近することもありますが、遠く離れることもあります。
マヌエル、

朝、私はあなたのコードを試しましたが(このスレッドの上記の投稿)、動作していないようです。どんな提案も歓迎します:-)
RRTW 2011

5

これは、この質問を見ている他の人に役立つかもしれません。Justinのコードを書き直して、メソッドが必要なターゲットサイズオブジェクトも受け取ることができるようにしました。これはCanvasを使用する場合に非常にうまく機能します。すべてのクレジットは、彼の優れた初期コードのためにジャスティンに行くべきです。

    private Bitmap getBitmap(int path, Canvas canvas) {

        Resources resource = null;
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            resource = getResources();

            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(resource, path, options);

            int scale = 1;
            while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                  IMAGE_MAX_SIZE) {
               scale++;
            }
            Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

            Bitmap pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeResource(resource, path, options);

                // resize to desired dimensions
                int height = canvas.getHeight();
                int width = canvas.getWidth();
                Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

                double y = Math.sqrt(IMAGE_MAX_SIZE
                        / (((double) width) / height));
                double x = (y / height) * width;

                Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;

                System.gc();
            } else {
                pic = BitmapFactory.decodeResource(resource, path);
            }

            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    }

ジャスティンのコードは、大きなビットマップを扱うオーバーヘッドを減らすのに非常に効果的です。


4

私のソリューションがベストプラクティスであるかどうかはわかりませんが、inDensityおよびinTargetDensityオプションを使用して、希望のスケーリングでビットマップをロードしました。inDensityある0描画可能リソースをロードしていない場合、最初ので、このアプローチは、非リソース・イメージをロードするためのものです。

変数imageUrimaxImageSideLengthおよびcontextは、私のメソッドのパラメーターです。明確にするために、AsyncTaskをラップせずにメソッド実装のみを投稿しました。

            ContentResolver resolver = context.getContentResolver();
            InputStream is;
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Options opts = new Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, opts);

            // scale the image
            float maxSideLength = maxImageSideLength;
            float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
            // do not upscale!
            if (scaleFactor < 1) {
                opts.inDensity = 10000;
                opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
            }
            opts.inJustDecodeBounds = false;

            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }

            return bitmap;

2
非常に素晴らしい!Bitmap.createScaledBitmapの代わりにinDensityを使用すると、メモリヒープを大幅に節約できました。inSamplesizeと組み合わせるとさらに便利です。
Ostkontentitan 2014年

2

正確なサイズにリサイズし、必要な品質を保ちたいということを考慮して、これを試してみるべきだと思います。

  1. BitmapFactory.decodeFileを呼び出し、checkSizeOptions.inJustDecodeBoundsを指定して、サイズ変更された画像のサイズを確認します
  2. デバイスでメモリを超えないように使用できる最大の inSampleSizeを計算します。bitmapSizeInBytes = 2 * width * height; 一般的にあなたの写真では、2 * 1944x1296)=4.8Mbбしか必要ないので、inSampleSize = 2で問題ありません。
  3. inSampleSizeでBitmapFactory.decodeFileを使用してビットマップをロードする
  4. ビットマップを正確なサイズにスケーリングします。

動機:複数ステップのスケーリングは、より高品質の画像を提供する可能性がありますが、高いinSampleSizeを使用するよりもうまく機能するという保証はありません。実際には、1つの操作で直接スケーリングを行うために、inSampleSizeを5(pow of 2ではない)のように使用することもできると思います。または、4を使用するだけで、UIでその画像を使用できます。サーバーに送信する場合-サーバー側で正確なサイズにスケーリングできるため、高度なスケーリング技法を使用できます。

注:手順3で読み込まれたビットマップが少なくとも4倍大きい場合(つまり、4 * targetWidth <width)、複数のサイズ変更を使用して品質を向上させることができます。少なくとも一般的なJavaでは機能しますが、Androidではスケーリングに使用される補間を指定するオプションがありません 。http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getscaledinstance.html


2

私はこのようなコードを使用しました:

  String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg";
  BitmapFactory.Options options=new BitmapFactory.Options();
  InputStream is=new FileInputStream(filePath);
  BitmapFactory.decodeStream(is, null, options);
  is.close();
  is=new FileInputStream(filePath);
  // here w and h are the desired width and height
  options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired...
  // bmp is the resized bitmap
  Bitmap bmp=BitmapFactory.decodeStream(is, null, options);
  is.close();
  Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

元の画像は1230 x 1230で、ビットマップは330 x 330
と表示されました。2590x 3849を試すと、OutOfMemoryErrorが発生します。

私はそれを追跡しましたが、元のビットマップが大きすぎる場合は、「BitmapFactory.decodeStream(is、null、options);」行でOutOfMemoryErrorをスローします...


2

上記のコードは少しきれいになりました。InputStreamsも最終的に閉じたラッピングを使用して、確実に閉じられるようにします。

*注意
入力:InputStreamは、int w、int hです。
出力:Bitmap

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }

1

ピクセルをスキップせずに「正しい」方法で画像をスケーリングするには、画像デコーダーにフックして、行ごとにダウンサンプリングを実行する必要があります。Android(およびその下にあるSkiaライブラリー)には、そのようなフックはありません。そのため、独自にフックする必要があります。jpegイメージを話していると仮定すると、Cでlibjpegを直接使用するのが最善です。

複雑さが伴うため、画像プレビュータイプのアプリでは、2段階のサブサンプル-次に再スケールを使用するのがおそらく最適です。



1

絶対に1ステップのサイズ変更を行いたい場合、android:largeHeap = trueであればビットマップ全体をロードすることができますが、ご覧のとおり、これは実際にお勧めできません。

ドキュメントから:android:largeHeapアプリケーションのプロセスを大きなDalvikヒープで作成する必要があるかどうか。これは、アプリケーション用に作成されたすべてのプロセスに適用されます。これは、プロセスにロードされた最初のアプリケーションにのみ適用されます。複数のアプリケーションがプロセスを使用できるようにするために共有ユーザーIDを使用している場合、すべてのユーザーが一貫してこのオプションを使用する必要があります。そうしないと、予期しない結果が生じます。ほとんどのアプリはこれを必要とせず、代わりに全体的なメモリ使用量を減らしてパフォーマンスを向上させることに焦点を当てる必要があります。これを有効にしても、利用可能なメモリの固定的な増加は保証されません。これは、一部のデバイスは、利用可能なメモリの合計によって制約されるためです。



0

これでうまくいきました。この関数は、sdカード上のファイルへのパスを取得し、表示可能な最大サイズのビットマップを返します。コードはOfirのもので、sd上の画像ファイルのようないくつかの変更がRessourceにあり、witdthとheigthはDisplay Objectから取得されます。

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}

0

Androidのメモリにある大きな画像をデコードする際に問題が発生しないコードを以下に示します。入力パラメーターが約1024x1024である限り、20MBを超える画像をデコードできました。返されたビットマップを別のファイルに保存できます。このメソッドの下には、画像を新しいビットマップにスケーリングするために使用する別のメソッドがあります。このコードは自由に使用してください。

/*****************************************************************************
 * public decode - decode the image into a Bitmap
 * 
 * @param xyDimension
 *            - The max XY Dimension before the image is scaled down - XY =
 *            1080x1080 and Image = 2000x2000 image will be scaled down to a
 *            value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image - a value of "null" if there is an issue decoding
 *         image dimension
 * 
 * @throws FileNotFoundException
 *             - If the image has been removed while this operation is
 *             taking place
 */
public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException
{
    // The Bitmap to return given a Uri to a file
    Bitmap bitmap = null;
    File file = null;
    FileInputStream fis = null;
    InputStream in = null;

    // Try to decode the Uri
    try
    {
        // Initialize scale to no real scaling factor
        double scale = 1;

        // Get FileInputStream to get a FileDescriptor
        file = new File( this.imageUri.getPath() );

        fis = new FileInputStream( file );
        FileDescriptor fd = fis.getFD();

        // Get a BitmapFactory Options object
        BitmapFactory.Options o = new BitmapFactory.Options();

        // Decode only the image size
        o.inJustDecodeBounds = true;
        o.inPreferredConfig = bitmapConfig;

        // Decode to get Width & Height of image only
        BitmapFactory.decodeFileDescriptor( fd, null, o );
        BitmapFactory.decodeStream( null );

        if( o.outHeight > xyDimension || o.outWidth > xyDimension )
        {
            // Change the scale if the image is larger then desired image
            // max size
            scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) );
        }

        // Decode with inSampleSize scale will either be 1 or calculated value
        o.inJustDecodeBounds = false;
        o.inSampleSize = (int) scale;

        // Decode the Uri for real with the inSampleSize
        in = new BufferedInputStream( fis );
        bitmap = BitmapFactory.decodeStream( in, null, o );
    }
    catch( OutOfMemoryError e )
    {
        Log.e( DEBUG_TAG, "decode : OutOfMemoryError" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "decode : NullPointerException" );
        e.printStackTrace();
    }
    catch( RuntimeException e )
    {
        Log.e( DEBUG_TAG, "decode : RuntimeException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "decode : FileNotFoundException" );
        e.printStackTrace();
    }
    catch( IOException e )
    {
        Log.e( DEBUG_TAG, "decode : IOException" );
        e.printStackTrace();
    }

    // Save memory
    file = null;
    fis = null;
    in = null;

    return bitmap;

} // decode

注:メソッドは、上記のcreateScaledBitmap呼び出しのデコードメソッドを除いて、互いに何の関係もありません。幅と高さが元の画像と異なる場合があります。

/*****************************************************************************
 * public createScaledBitmap - Creates a new bitmap, scaled from an existing
 * bitmap.
 * 
 * @param dstWidth
 *            - Scale the width to this dimension
 * @param dstHeight
 *            - Scale the height to this dimension
 * @param xyDimension
 *            - The max XY Dimension before the original image is scaled
 *            down - XY = 1080x1080 and Image = 2000x2000 image will be
 *            scaled down to a value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image scaled - a value of "null" if there is an issue
 * 
 */
public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig )
{
    Bitmap scaledBitmap = null;

    try
    {
        Bitmap bitmap = this.decode( xyDimension, bitmapConfig );

        // Create an empty Bitmap which will contain the new scaled bitmap
        // This scaled bitmap should be the size we want to scale the
        // original bitmap too
        scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig );

        float ratioX = dstWidth / (float) bitmap.getWidth();
        float ratioY = dstHeight / (float) bitmap.getHeight();
        float middleX = dstWidth / 2.0f;
        float middleY = dstHeight / 2.0f;

        // Used to for scaling the image
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale( ratioX, ratioY, middleX, middleY );

        // Used to do the work of scaling
        Canvas canvas = new Canvas( scaledBitmap );
        canvas.setMatrix( scaleMatrix );
        canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) );
    }
    catch( IllegalArgumentException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" );
        e.printStackTrace();
    }

    return scaledBitmap;
} // End createScaledBitmap

スケールの検出力計算はここでは単に間違っています。アンドロイドドコページの計算を使用してください。
Fattie 2014

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

または:

 resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);

0

Integer.numberOfLeadingZerosは最高のサンプルサイズ、より良いパフォーマンスを計算するために使用します。

kotlinの完全なコード:

@Throws(IOException::class)
fun File.decodeBitmap(options: BitmapFactory.Options): Bitmap? {
    return inputStream().use {
        BitmapFactory.decodeStream(it, null, options)
    }
}

@Throws(IOException::class)
fun File.decodeBitmapAtLeast(
        @androidx.annotation.IntRange(from = 1) width: Int,
        @androidx.annotation.IntRange(from = 1) height: Int
): Bitmap? {
    val options = BitmapFactory.Options()

    options.inJustDecodeBounds = true
    decodeBitmap(options)

    val ow = options.outWidth
    val oh = options.outHeight

    if (ow == -1 || oh == -1) return null

    val w = ow / width
    val h = oh / height

    if (w > 1 && h > 1) {
        val p = 31 - maxOf(Integer.numberOfLeadingZeros(w), Integer.numberOfLeadingZeros(h))
        options.inSampleSize = 1 shl maxOf(0, p)
    }
    options.inJustDecodeBounds = false
    return decodeBitmap(options)
}

-2

次のコードを使用してビットマップのサイズを変更します

    public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;        
    BitmapFactory.decodeFile(file.getPath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(file.getPath(), options);
   }

    private static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }

     return inSampleSize;
   }    

同じことが次のヒント/トリックでも説明されています

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.