web-dev-qa-db-fra.com

android réduire la taille de fichier pour que l'image capturée par la caméra soit inférieure à 500 kb

Mon exigence est de télécharger une image capturée de la caméra sur le serveur, mais celle-ci devrait être inférieure à 500 Ko. S'il dépasse 500 Ko, il doit être réduit à une taille inférieure à 500 Ko (mais un peu plus près)

Pour cela, j'utilise le code suivant - 

@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);

        }
    }

Et

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;
        }
    }

Pour faire pivoter l'image, si nécessaire.

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;
    }

Voici la sortie de la taille du fichier image initialement et après toutes les opérations pour réduire la taille du fichier.

img_file_size size taille du fichier en Ko (initialement): 3294 

img_file_size size taille du fichier en Ko après manipulation de l'image: 235

Voir la différence ci-dessus (dans la sortie). La taille de fichier initiale sans ces opérations, et après ces opérations de compression et autres. J'ai besoin que cette taille soit un peu plus proche de 500 Ko.

Le code ci-dessus fonctionne assez bien pour moi, car il réduit la taille du fichier image pour le rendre inférieur à 500 Ko.

Mais, voici les problèmes avec le code ci-dessus -

  • Ce code réduit la taille du fichier même si sa taille est inférieure à 500 Ko.

  • Si la taille du fichier est supérieure à 500 Ko, la taille réduite du fichier devient trop faible à partir de 500 Ko, bien que j'en ai besoin d'un peu plus près.

Je dois me débarrasser de plus de 2 problèmes. Donc, besoin de savoir ce que je devrais manipuler dans le code ci-dessus.

Comme je veux aussi corriger l'orientation EXIF ​​(images pivotées), ainsi que mon exigence mentionnée ci-dessus.

20
NarendraJi

Vous pouvez vérifier la taille avant de la redimensionner. Si la taille du bitmap est supérieure à 500 Ko, redimensionnez-la.

De même, pour que les images bitmap plus grandes se rapprochent de la taille de 500 Ko, vérifiez la différence entre la taille et la compression en conséquence.

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

et en redimensionnant Bitmap calculer la différence de taille et compresser en conséquence

4
Cold Fire

Un autre problème que vous avez est que vous mesurez la taille du bitmap (qui n'est pas compressé), puis le convertissez au format JPG et le mesurez. Le format JPG sera probablement toujours plus petit et sa compression dépendra de la qualité de l’image. Beaucoup de grandes zones de la même couleur? Génial! Un modèle extrêmement "occupé"? La compression ne sera pas aussi grande.

OK, donc plus de précisions:

Si vous ciblez une certaine taille de fichier, vous ne pouvez pas le faire en regardant la taille AVANT la compression. Vous pouvez avoir une idée générale (par exemple, les compressions JPEG par un facteur d'environ 15 pour ces photos), de sorte que vous pouvez cibler 500 000 * 15 pour le bitmap. Mais selon le contenu de la photo, vous risquez de ne pas atteindre exactement cette cible. Donc, vous voulez probablement faire ceci:

  1. Choisissez un jpegFactor
  2. bitMapTarget = target * jpegFactor 
  3. taille de la carte de réglage pour s'adapter à bitMapTarget 
  4. compresser bitmap en JPEG 
  5. S'il est toujours au-dessus de la cible, ajustez jpegFactor et réessayez.

Vous pourriez avoir quelques étapes sur le point 5 pour déterminer votre distance et essayer de prendre cela en compte.

1
bryan

vous pouvez essayer cette méthode 

    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);

c'est utile pour vous.

1
Atul Mavani

Pour réduire la taille de Image, j'ai utilisé ce code .. et son travail pour moi .. Veuillez le vérifier une fois .. Cela pourrait vous être utile ..

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

Personnaliser mon getResizedBitmapLessThan500KB () à cela ci-dessous, a fonctionné pour moi.

    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
NarendraJi

Ce n'est pas la solution à votre problème mais le bogue qui explique pourquoi vous obtenez de très petits fichiers.

Dans getResizedBitmapLessThan500KB(photo, 500), le 500 correspond au maximum avec/hauteur en pixel de l’image et non à la taille maximale en kb.

Donc, tous les fichiers compressés font moins de 500x500 pixels

1
k3b

Veuillez vérifier si ce code est utile:

    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

Je suppose que vous voulez utiliser la galerie ou la caméra système pour obtenir l’image. Veuillez noter qu'il existe une limite supérieure pour le maximum de données transférées via Intent. C'est pourquoi vous obtenez toujours une version réduite des images. 

Vous pouvez vous référer à https://developer.Android.com/training/camera/photobasics.html pour la solution standard. En résumé, vous devez acquérir l'accès au stockage externe et générer une URI pour caméra ou obtenir la URI à partir de l'application de la galerie. Ensuite, utilisez la ContentResolver pour obtenir l'image. 

InputStream inputStream = mContentResolver.openInputStream(mUri);

Vous souhaiterez peut-être implémenter le résolveur de contenu pour que d'autres applications accèdent à vos données, ce qui est la pratique habituelle. 

0
zhaocong