web-dev-qa-db-de.com

android reduziert die Dateigröße für ein von der Kamera aufgenommenes Bild auf weniger als 500 KB

Meine Anforderung besteht darin, das von der Kamera aufgenommene Bild auf den Server hochzuladen, es sollte jedoch weniger als 500 KB betragen. Wenn es größer als 500 KB ist, muss es auf eine Größe von weniger als 500 KB reduziert werden (aber etwas näher)

Dafür verwende ich den folgenden Code - 

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

        }
    }

Und

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

Um das Bild zu drehen, falls erforderlich.

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

Hier wird zunächst die Bilddateigröße ausgegeben und anschließend alle Operationen zur Reduzierung der Dateigröße.

img_file_size Dateigröße in KB (anfangs): 3294 

img_file_size ﹕ Dateigröße in KB nach Bildmanipulationen: 235

Siehe den Unterschied oben (in der Ausgabe). Die anfängliche Dateigröße ohne diese Vorgänge sowie nach dieser Komprimierung und anderen Vorgängen. Ich brauche diese Größe, um näher an 500 kb zu sein.

Der obige Code funktioniert für mich einigermaßen gut, da er die Dateigröße reduziert, um weniger als 500 KB zu erhalten.

Aber das Folgende sind die Probleme mit dem obigen Code -

  • Dieser Code reduziert die Dateigröße, auch wenn sie weniger als 500 KB beträgt

  • Wenn es mehr als 500 KB ist, wird die reduzierte Dateigröße von 500 KB zu gering, obwohl ich sie etwas näher brauche.

Ich muss über 2 Ausgaben loswerden. Ich muss also wissen, was ich im obigen Code manipulieren soll.

Da ich auch die EXIF-Ausrichtung korrigieren möchte (gedrehte Bilder), zusammen mit meiner oben genannten Anforderung.

20
NarendraJi

Sie können die Größe überprüfen, bevor Sie die Größe ändern. Wenn die Bitmap größer als 500 KB ist, ändern Sie die Größe.

Überprüfen Sie auch den Unterschied zwischen den Größen, und komprimieren Sie entsprechend, um eine größere Bitmap näher als 500 KB zu machen.

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

und in Größe ändern Bitmap berechnen die Differenz in der Größe und komprimieren Sie entsprechend

4
Cold Fire

Ein anderes Problem besteht darin, dass Sie die Größe der Bitmap (die nicht komprimiert ist) messen und diese dann in JPG konvertieren und messen. Das JPG wird wahrscheinlich immer kleiner sein, und wie gut es komprimiert wird, wird ein Faktor für das Bild sein. Viele große Bereiche der gleichen Farbe? Großartig! Ein extrem "beschäftigt" Muster? Kompression wird nicht so groß sein.

OK, also weitere Klarstellung:

Wenn Sie auf eine bestimmte Dateigröße abzielen, können Sie dies nicht tun, indem Sie die Größe vor der Komprimierung betrachten. Sie könnten eine allgemeine Vorstellung erhalten (z. B. JPEG-Komprimierungen für diese Fotos um den Faktor ~ 15). Dann könnten Sie 500 Bit * 15 für die Bitmap festlegen. Aber je nachdem, was auf dem Foto ist, treffen Sie dieses Ziel möglicherweise nicht genau. Also möchten Sie wahrscheinlich folgendes tun:

  1. Wählen Sie einen JPEG-Faktor aus
  2. bitMapTarget = target * jpegFactor 
  3. passen Sie die Bitmap-Größe an das BitMapTarget an 
  4. bitmap zu JPEG komprimieren 
  5. Wenn sich dieser immer noch über dem Ziel befindet, passen Sie den jpegFactor an und versuchen Sie es erneut.

Sie könnten einige Schritte auf # 5 ausführen, um herauszufinden, wie nahe Sie waren und versuchen, dies zu berücksichtigen.

1
bryan

sie können diese Methode ausprobieren 

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

es ist hilfreich für Sie.

1
Atul Mavani

Um die Größe des Bildes zu reduzieren, habe ich diesen Code verwendet .. und seine Arbeit für mich ... Bitte überprüfen Sie es einmal.

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

Anpassen meiner getResizedBitmapLessThan500KB () auf diese unten, hat für mich funktioniert.

    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

Dies ist nicht die Lösung für Ihr Problem, sondern der Fehler, warum Sie sehr kleine Dateien erhalten.

In getResizedBitmapLessThan500KB(photo, 500) ist 500 die maximale Höhe mit/height in Pixel des Bildes, nicht die maximale Größe in kb.

Daher haben alle komprimierten Dateien weniger als 500 x 500 Pixel

1
k3b

Bitte überprüfen Sie, ob dieser Code hilfreich ist:

    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

Ich gehe davon aus, dass Sie die Galerie oder Systemkamera verwenden möchten, um das Bild zu erhalten. Bitte beachten Sie, dass es eine Obergrenze für die maximalen Daten gibt, die über Intent übertragen werden. Aus diesem Grund erhalten Sie immer eine verkleinerte Version von Bildern. 

Sie können auf https://developer.Android.com/training/camera/photobasics.html für die Standardlösung verweisen. Zusammenfassend sollten Sie den Zugriff auf externen Speicher erwerben und eine URI für die Kamera generieren oder die URI von der Galerie-App abrufen. Verwenden Sie dann die Variable ContentResolver, um das Bild abzurufen. 

InputStream inputStream = mContentResolver.openInputStream(mUri);

Möglicherweise möchten Sie den Inhaltsauflöser für andere Anwendungen implementieren, um auf Ihre Daten zuzugreifen. Dies ist die übliche Vorgehensweise. 

0
zhaocong