2017-04-18 3 views
0

J'ai une petite méthode qui est destinée à redimensionner une image (stockée comme un fichier) pour produire une nouvelle image avec une taille de fichier prédéfinie (dans mon cas 512 KB). La méthode ressemble à quelque chose comme ce qui suit:Android - réduire les dimensions de l'image ne réduit pas la taille dans la même proportion

private static final int maximumFileSizeInKBs = 512; 

public static Uri resizeImage(Context context, Uri imageUri) { 
    // get image width and height 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    Bitmap imageBitmap = BitmapFactory.decodeFile(new File(imageUri.getPath()).getAbsolutePath(), options); 
    int imageWidth = options.outWidth; 
    int imageHeight = options.outHeight; 

    // get image file size (in KBs) 
    File file = new File(imageUri.getPath()); 
    int fileSizeInKBs = Integer.parseInt(String.valueOf(file.length()/1024)); 

    // get the scaling factor 
    // (square root because when we scale both the width and height by the 
    // square root of the scaling factor, then the size of the final image 
    // will be scaled by the scaling factor) 
    double scalingFactor = Math.sqrt(fileSizeInKBs/maximumFileSizeInKBs); 

    // no need to scale if file already within maximum file size limit 
    if(scalingFactor <= 1) { 
     return imageUri; 
    } 

    // get scaled dimensions 
    int scaledImageWidth = (int) Math.floor(imageWidth/scalingFactor); 
    int scaledImageHeight = (int) Math.floor(imageHeight/scalingFactor); 

    // load original bitmap (load a scaled copy to avoid OutOfMemory errors) 
    options = new BitmapFactory.Options(); 
    options.inSampleSize = Math.max(imageHeight/scaledImageWidth, imageHeight/scaledImageHeight); 
    imageBitmap = BitmapFactory.decodeFile(new File(imageUri.getPath()).getAbsolutePath(), options); 

    // the scaled image above will be scaled by a value equal to the 
    // nearest power of 2, hence it wont be scaled to the exact value 
    // that we want. So, we need to calculate new scaling factors 
    // based on the scaled loaded bitmap 
    Matrix m = new Matrix(); 
    RectF inRect = new RectF(0, 0, imageWidth, imageHeight); 
    RectF outRect = new RectF(0, 0, scaledImageWidth, scaledImageHeight); 
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); 
    float[] values = new float[9]; 
    m.getValues(values); 

    // create a scaled bitmap with required file size 
    Bitmap scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, (int) (imageWidth*values[0]), (int) (imageHeight*values[4]), true); 

    // delete original image 
    new File(imageUri.getPath()).delete(); 

    // write bitmap to file 
    File newImageFile = BaseResourceClass.makeTempResourceFile(Slide.ResourceType.IMAGE, context); 
    try { 
     newImageFile.createNewFile(); 
     FileOutputStream fos = new FileOutputStream(newImageFile); 
     scaledBitmap.compress(Bitmap.CompressFormat.PNG, 1, fos); 
     fos.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     Toast.makeText(context, "Something went wrong", Toast.LENGTH_SHORT).show(); 
    } 

    // recycle bitmaps to reallocate memory 
    Storage.recycleBitmap(imageBitmap); 
    Storage.recycleBitmap(scaledBitmap); 

    // scaling done, return new Image 
    return Uri.fromFile(newImageFile); 
} 

Lorsque cette méthode fonctionne, les dimensions de l'image se réduit à la taille exacte (comme dans ce que je pense que ce soit), mais la taille de l'image en Ko de ne sont pas revus à la baisse par le même facteur. Par exemple, après l'exécution de cette méthode, je reçois les numéros suivants ce

before resizing 
imageWidth : 3120, imageHeight : 4160, fileSize : 2960 KB 

after resizing 
imageWidth : 1395, imageHeight : 1860, fileSize : 2491 KB 

Je ne comprends pas après avoir enlevé tant de pixels de l'image, est-ce qui prend tant de place de façon à maintenir la taille du fichier image autant. Je ai essayé de regarder d'autres questions StackOverflow et la documentation des classes BitmapFactory et Bitmap Android, mais avec ce problème étant mentionné nulle part. Toute aide serait appréciée!

Répondre

1

Il y a beaucoup de raisons pour lesquelles cela se produit, et cela dépend du type d'image que vous lisez (JPG, GIF, PNG, BMP), de la qualité avec laquelle elle a été compressée (si c'était JPG) et comment vous l'enregistrez après avoir réduit les dimensions de l'image.

Dans votre exemple, vous n'indiquez pas l'image utilisée ou le type de compression utilisé par l'image. Mais à partir du code, je vois que vous l'enregistrez comme une image PNG. Si vous prenez une image jpg de qualité supérieure de 2 Mo, et que vous réduisez la taille comme vous l'avez fait, mais que vous l'enregistrez au format PNG (qualité perdue), vous obtiendrez probablement un fichier plus volumineux qu'auparavant. Les images JPG utilisent des algorithmes de compression pour stocker une image aussi proche que possible de l'original mais occupant beaucoup moins d'espace. Selon la qualité, vous pouvez obtenir une image qui ressemble presque à l'original avec une grande taille de fichier, ou une image horrible qui ne prend que quelques kilo-octets. Lorsque vous enregistrez une image au format PNG, vous obtenez exactement la même image que votre original (c'est pourquoi elle est appelée sans perte), et de cette façon, vous ne pouvez pas enregistrer beaucoup de fichiers par compression. Si vous utilisez un PNG en entrée et enregistrez une version réduite au format PNG, vous serez probablement beaucoup plus proche de votre idée de «taille de fichier proportionnelle». Bien que la réduction de la taille puisse créer de nouvelles couleurs dans l'image résultante grâce à l'anti-crénelage et réduire un peu les performances de compression.

L'utilisation d'images JPG rend le résultat un peu plus imprévisible, car la qualité utilisée dans l'image d'origine et celle utilisée pour compresser le résultat ont une incidence sur la taille du fichier.

0

Vous pouvez vous attendre à ce que la taille de votre fichier image soit proportionnelle à la largeur/hauteur de l'image si l'image n'est pas compressée. C'est vrai pour le format BMP seulement.

Vous traitez des images qui sont stockées au format PNG. PNG est la compression d'utilisation pour stocker des images. C'est pourquoi la taille de l'image dépend plus de la quantité de détails de votre image et moins de la largeur de l'image & hauteur.

Dans votre cas, la taille de l'image dépend le plus du second paramètre, la qualité.

scaledBitmap.compress (Bitmap.CompressFormat.PNG, , fos);

Vous pouvez le modifier à 0 pour avoir un fichier plus petit.