web-dev-qa-db-ja.com

androidカメラでキャプチャした画像のファイルサイズを500 kb未満に減らします

私の要件は、カメラでキャプチャした画像をサーバーにアップロードすることですが、500 KB未満にする必要があります。場合、500 KBより大きい場合は、500 KB未満のサイズに縮小する必要があります(ただし、多少近い)

このために、私は次のコードを使用しています-

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        try {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == getActivity().RESULT_OK) {

                    if (requestCode == REQUEST_CODE_CAMERA) {

                        try {

                            photo = MediaStore.Images.Media.getBitmap(
                                    ctx.getContentResolver(), capturedImageUri);
                            String selectedImagePath = getRealPathFromURI(capturedImageUri);

                            img_file = new File(selectedImagePath);

                            Log.d("img_file_size", "file size in KBs (initially): " + (img_file.length()/1000));

                            if(CommonUtilities.isImageFileSizeGreaterThan500KB(img_file)) {
                                photo = CommonUtilities.getResizedBitmapLessThan500KB(photo, 500);
                            }
                            photo = CommonUtilities.getCorrectBitmap(photo, selectedImagePath);


//  // CALL THIS METHOD TO GET THE URI FROM THE BITMAP

                            img_file = new File(ctx.getCacheDir(), "image.jpg");
                            img_file.createNewFile();

//Convert bitmap to byte array
                            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                            photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

//write the bytes in file
                            FileOutputStream fo = new FileOutputStream(img_file);
                            fo.write(bytes.toByteArray());

// remember close de FileOutput
                            fo.close();
                            Log.d("img_file_size", "file size in KBs after image manipulations: " + (img_file.length()/1000));


                        } catch (Exception e) {
                            Logs.setLogException(class_name, "onActivityResult(), when captured from camera", e);
                        }


                    } 

            }
        } catch (Exception e) {
            Logs.setLogException(class_name, "onActivityResult()", e);
        } catch (OutOfMemoryError e) {
            Logs.setLogError(class_name, "onActivityResult()", e);

        }
    }

そして

public static Bitmap getResizedBitmapLessThan500KB(Bitmap image, int maxSize) {
        int width = image.getWidth();
        int height = image.getHeight();



        float bitmapRatio = (float)width / (float) height;
        if (bitmapRatio > 0) {
            width = maxSize;
            height = (int) (width / bitmapRatio);
        } else {
            height = maxSize;
            width = (int) (height * bitmapRatio);
        }
        Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        if(sizeOf(reduced_bitmap) > (500 * 1000)) {
            return getResizedBitmap(reduced_bitmap, maxSize);
        } else {
            return reduced_bitmap;
        }
    }

必要に応じて画像を回転します。

public static Bitmap getCorrectBitmap(Bitmap bitmap, String filePath) {
        ExifInterface ei;
        Bitmap rotatedBitmap = bitmap;
        try {
            ei = new ExifInterface(filePath);

            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            Matrix matrix = new Matrix();
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    matrix.postRotate(90);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    matrix.postRotate(180);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    matrix.postRotate(270);
                    break;
            }

            rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return rotatedBitmap;
    }

以下は、初期およびすべての操作を行った後のファイルサイズを縮小するための画像ファイルサイズの出力です。

img_file_size:ファイルサイズ(KB)(初期):3294

img_file_size:画像操作後のファイルサイズ(KB):235

上記の違いを(出力で)参照してください。これらの操作なしの初期ファイルサイズ、およびそれらの圧縮およびその他の操作後のサイズ。500kbに​​いくらか近づける必要があります。

上記のコードは、画像ファイルのサイズを500 KB未満に縮小しているため、私にとっては多少問題なく機能しています。

しかし、以下は上記のコードの問題です-

  • このコードは、500 KB未満であってもファイルサイズを縮小しています

  • 500 KBを超えると、ファイルサイズが500 KBから小さくなりすぎますが、少し近づける必要があります。

2つ以上の問題を取り除く必要があります。したがって、上記のコードで何を操作する必要があるかを知る必要があります。

上記の要件に加えて、EXIF方向(回転した画像)も修正したいので。

21
Narendra Singh

サイズを変更する前にサイズを確認できます。ビットマップのサイズが500kbより大きい場合は、サイズを変更します。

また、大きなビットマップを500kbに​​近いサイズにするには、サイズの違いを確認し、それに応じて圧縮します。

if(sizeOf(reduced_bitmap) > (500 * 1024)) {
    return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
} else {
    return reduced_bitmap;
}

ビットマップのサイズ変更では、サイズの違いを計算し、それに応じて圧縮します

4
ColdFusion

もう1つの問題は、ビットマップ(圧縮されていない)のサイズを測定し、それをJPGに変換して測定していることです。 JPGは常に小さくなり、圧縮率は画像の要素の1つになります。同じ色の大きな領域がたくさんありますか?すごい!非常に「忙しい」パターンですか?圧縮はそれほど良くありません。

わかりましたので、さらに明確にします:

特定のファイルサイズを対象にしている場合、圧縮前にサイズを確認することはできません。一般的なアイデア(たとえば、これらの写真ではJPEGが約15倍に圧縮される)を得ることができるため、ビットマップを500k * 15にすることができます。しかし、写真の内容によっては、そのターゲットに正確に当たらない場合があります。だからあなたはおそらくこれをしたいでしょう:

  1. JpegFactorを選択します
  2. bitMapTarget = target * jpegFactor
  3. bitMapTargetに合わせてビットマップサイズを調整する
  4. ビットマップをJPEGに圧縮
  5. これがまだターゲットを超えている場合は、jpegFactorを調整して再試行してください。

#5でいくつかの手順を実行して、自分がどれだけ近いかを把握し、それを考慮に入れることができます。

1
bryan

あなたはこの方法を試すことができます

    public static Bitmap getScaledBitmap(Bitmap b, int reqWidth, int reqHeight)
        {
            Matrix m = new Matrix();
            m.setRectToRect(new RectF(0, 0, b.getWidth(), b.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
            return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
        }

//call this method like
    Bitmap bm400=getScaledBitmap(bm,500,500);

あなたに役立ちます。

1
Atul Mavani

Imageのサイズを小さくするために、このコードを使用しました。私にとってはその作業です。一度確認してください。参考になるかもしれません。

Bitmap photo1 ;
private byte[] imageByteArray1 ;


BitmapFactory.Options opt1 = new BitmapFactory.Options();
opt1.inJustDecodeBounds=true;
BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),opt1);

// The new size we want to scale to
final int REQUIRED_SIZE=320;

// Find the correct scale value. It should be the power of 2.
int width_tmp=opt1.outWidth,height_tmp=opt1.outHeight;
int scale=2;
while(true){
    if(width_tmp>REQUIRED_SIZE||height_tmp>REQUIRED_SIZE)
        break;
    width_tmp/=2;
    height_tmp/=2;
    scale*=2;
}
// Decode with inSampleSize
BitmapFactory.Options o2=new BitmapFactory.Options();
o2.inSampleSize=scale;
o2.inJustDecodeBounds=false;
photo1=BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),o2);

ByteArrayOutputStream baos1=new ByteArrayOutputStream();
photo1.compress(Bitmap.CompressFormat.JPEG,60,baos1);
imageByteArray1=baos1.toByteArray();
1
Preet_Android

getResizedBitmapLessThan500KB()を以下のようにカスタマイズすると、うまくいきました。

    public static final long CAMERA_IMAGE_MAX_DESIRED_SIZE_IN_BYTES = 2524970;
        public static final double CAMERA_IMAGE_MAX_SIZE_AFTER_COMPRESSSION_IN_BYTES = 1893729.0;

 public static Bitmap getResizedBitmapLessThan500KB(Bitmap image, int maxSize, long file_size_in_bytes) {
                int width = image.getWidth();
                int height = image.getHeight();


                if(file_size_in_bytes <= AppGlobalConstants.CAMERA_IMAGE_MAX_DESIRED_SIZE_IN_BYTES) {
                    if (width > height) {
                        if (width > 500)
                            maxSize = width * 75 / 100;
                    } else {
                        if (height > 500)
                            maxSize = height * 75 / 100;
                    }
                } else {
                    double percentage = ((AppGlobalConstants.CAMERA_IMAGE_MAX_SIZE_AFTER_COMPRESSSION_IN_BYTES/file_size_in_bytes)*100);
                    if (width > height) {
                        if (width > 500)
                            maxSize = width * (int)percentage / 100;
                    } else {
                        if (height > 500)
                            maxSize = height * (int)percentage / 100;
                    }

                    if(maxSize > 600) {
                        maxSize = 600;
                    }

                }

                float bitmapRatio = (float)width / (float) height;
                if (bitmapRatio > 0) {
                    width = maxSize;
                    height = (int) (width / bitmapRatio);
                } else {
                    height = maxSize;
                    width = (int) (height * bitmapRatio);
                }
                Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        //        Log.d("file_size","file_size_during_manipulation: "+String.valueOf(sizeOf(reduced_bitmap)));
                if(sizeOf(reduced_bitmap) > (500 * 1000)) {
                    return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
                } else {
                    return reduced_bitmap;
                }
            }
1
Narendra Singh

これは問題の解決策ではありませんが、非常に小さなファイルを取得するバグです。

getResizedBitmapLessThan500KB(photo, 500)では、500は画像の最大サイズ/高さ(ピクセル単位)ではなく、kb単位の最大サイズです。

したがって、すべての圧縮ファイルは500x500ピクセル未満です

1
k3b

このコードが役立つかどうかを確認してください:

    final static int COMPRESSED_RATIO = 13;
    final static int perPixelDataSize = 4;
    public byte[] getJPGLessThanMaxSize(Bitmap image, int maxSize){
        int maxPixelCount = maxSize *1024 * COMPRESSED_RATIO / perPixelDataSize;
        int imagePixelCount = image.getWidth() * image.getHeight();
        Bitmap reducedBitmap;
        // Adjust Bitmap Dimensions if necessary.
        if(imagePixelCount > maxPixelCount) reducedBitmap = getResizedBitmapLessThanMaxSize(image, maxSize);
        else reducedBitmap = image;

        float compressedRatio = 1;
        byte[] resultBitmap;
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        int jpgQuality = 100;
        // Adjust Quality until file size is less than maxSize.
        do{
            reducedBitmap.compress(Bitmap.CompressFormat.JPEG, jpgQuality, outStream);
            resultBitmap = outStream.toByteArray();
            compressedRatio = resultBitmap.length / (reducedBitmap.getWidth() * reducedBitmap.getHeight() * perPixelDataSize);
            if(compressedRatio > (COMPRESSED_RATIO-1)){
                jpgQuality -= 1;
            }else if(compressedRatio > (COMPRESSED_RATIO*0.8)){
                jpgQuality -= 5;
            }else{
                jpgQuality -= 10;
            }
        }while(resultBitmap.length > (maxSize * 1024));
        return resultBitmap;
    }

    public Bitmap getResizedBitmapLessThanMaxSize(Bitmap image, int maxSize) {
        int width = image.getWidth();
        int height = image.getHeight();
        float bitmapRatio = (float)width / (float) height;

        // For uncompressed bitmap, the data size is:
        // H * W * perPixelDataSize = H * H * bitmapRatio * perPixelDataSize
        // 
        height = (int) Math.sqrt(maxSize * 1024 * COMPRESSED_RATIO / perPixelDataSize / bitmapRatio);
        width = (int) (height * bitmapRatio);
        Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        return reduced_bitmap;
    }
0
i_A_mok

ギャラリーまたはシステムカメラを使用して画像を取得することを想定しています。 Intentを介して転送される最大データには上限があるため、常に画像のサイズが縮小されたバージョンになることに注意してください。

標準ソリューションについては、 https://developer.Android.com/training/camera/photobasics.html を参照できます。要約すると、外部ストレージへのアクセス権を取得してカメラ用のURIを生成するか、ギャラリーアプリからURIを取得する必要があります。次に、ContentResolverを使用して画像を取得します。

InputStream inputStream = mContentResolver.openInputStream(mUri);

他のアプリケーションがデータにアクセスするためのコンテンツリゾルバーを実装することもできますが、これは標準的な方法です。

0
zhaocong