Android: сжатие растровых изображений вызывает проблемы с нехваткой памяти

Я пытаюсь сжать растровое изображение, выбранное из галереи или снятое камерой, перед отправкой на сервер. Теперь я делаю это, выполнив следующие шаги:

1) получить путь к изображению.

2) получить растровое изображение по этому пути.

3) проверьте исходную ширину и высоту растрового изображения и сравните их с максимальной шириной и высотой, а затем используйте коэффициент отношения для настройки ширины и высоты.

4) проверить и правильно повернуть растровое изображение с помощью exif.

5) окончательно уменьшите качество, чтобы получить окончательное сжатое растровое изображение.

Код:

    private static final float max_width = 1280.0f;
    private static final float max_height = 1280.0f;
    private static final float max_ratio = max_width/max_height; 

    private void CompressImage(String path , Bitmap bitmap_original){



     //get width and height of original bitmap

     int original_width = bitmap_original.getWidth();
     int original_height = bitmap_origianl.getHeight();

     float original_ratio = (float)width/(float)height;

     if(original_height > max_height || original_width > max_width){
      //adjust bitmap dimensions

       int width = 0;
       int height = 0;

       if(original_ratio<max_ratio){
       width= (int)((max_height/original_height)*original_width);
       height=(int) max_height;  

       }else if(original_ratio>max_ratio){
        height= (int)((max_width/original_width)*original_height);
        width= (int)max_width;
       }else{
         height = max_height; 
         width = max_width;  
       }

       //adjust the bitmap 
        Bitmap adjusted_bitmap = Bitmap.createScaledBitmap(bitmap_original,width,height,false);


       //check rotation
       Matrix matrix = new Matrix();  
       try{
       ExifInterface exif = new ExifInterface(path);
       int original_orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION , ExifInterface.ORIENTATION_UNDEFINED);

         if(original_orientation == ExifInterface.ORIENTATION_ROTATE_90){

          matrix.setRotate(90);

         }else if(original_orientation == ExifInterface.ORIENTATION_180){

          matrix.setRotate(180);

         }else if(original_orientation == ExifInterface.ORIENTATION_270){

          matrix.setRotate(270);
         }

         Bitmap rotated_adjusted_bitmap = Bitmap.createBitmap(adjusted_bitmap , 0 , 0 , adjusted_bitmap.getWidth(), adjusted_bitmap.getHeight(),matrix,true);


         //lower the quality

          ByteArrayOutputStream out = new ByteArrayOutputStream();
          adjusted_rotated_bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
          Bitmap adjusted_rotated_lowquality_bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));  

       }catch(IOException e){


        }


      }else{
      //keep bitmap as is
      Bitmap adjusted_bitmap = bitmap_original;
      }



     }

Проблема

Все вышеперечисленное работает нормально, но проблема в том, что в некоторых случаях приложение аварийно завершает работу с исключением Out Of Memory.

Что-то не так с моим кодом?

Я мало что знаю о растровых изображениях и сжатии.

Спасибо за вашу помощь.


person Community    schedule 04.05.2018    source источник
comment
Возможный дубликат stackoverflow.com/questions/477572/   -  person Maddy Blacklisted    schedule 04.05.2018
comment
Вылетает при отображении изображения? Или вы отправляете изображение на сервер без отображения?   -  person Md Sufi Khan    schedule 04.05.2018
comment
@MdSufiKhan просто сжимающая часть иногда блокирует пользовательский интерфейс из-за ошибки нехватки памяти.   -  person    schedule 04.05.2018
comment
@MdSufiKhan отображение обрабатывается скольжением, но я не знаю, почему сжатие иногда не работает ... может быть, это потому, что растровое изображение имеет высокое разрешение.   -  person    schedule 04.05.2018
comment
Это точно растровое изображение в высоком разрешении. Вы пытались сжать в отдельном потоке?   -  person Md Sufi Khan    schedule 04.05.2018
comment
@MdSufiKhan да, это обрабатывается в задаче фонового потока (асинхронной) .... но я просто хочу спросить вас, есть ли что-то не так с приведенным выше кодом. Как убедиться, что у меня не заканчивается память для любого изображения?   -  person    schedule 04.05.2018
comment
Вы загружаете растровое изображение перед сжатием. Я думаю, что это основная причина ошибки «Недостаточно памяти». Вы можете ознакомиться с этим stackoverflow.com/a/31612736/2728085.   -  person Md Sufi Khan    schedule 04.05.2018
comment
Давайте продолжим обсуждение в чате.   -  person Md Sufi Khan    schedule 04.05.2018
comment
@MdSufiKhan, что вы имеете в виду под загрузкой растрового изображения перед сжатием? Иначе каков правильный путь? Спасибо.   -  person    schedule 04.05.2018


Ответы (2)


Bitmap обычно требует времени и иногда блокирует ввод-вывод. Поэтому не рекомендуется вызывать imageWithText в потоке пользовательского интерфейса.

Glide спроектирован так, чтобы быть гибким, и эта проблема очень хорошо демонстрирует эту черту.

«Скользите» ниже, что я очень рекомендую.

ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
yourBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); 
Glide.with(this).load(stream.toByteArray())
     .asBitmap() .error(R.drawable.ic_thumb_placeholder) 
     .transform(new CircleTransform(this)).into(imageview);

Для получения дополнительной информации проверьте ссылку ниже

person Fazal Hussain    schedule 04.05.2018
comment
как это должно сжимать растровое изображение перед отправкой на сервер? - person ; 04.05.2018

Вы можете получить URI изображения при выборе изображения из галереи как:

Uri myUri = data.getData();

Перед отправкой изображения на сервер вам необходимо преобразовать этот uri в byteArray[]

как показано ниже:

    Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),myUri);

   ByteArrayOutputStream baos = new ByteArrayOutputStream();

   bitmap.compress(Bitmap.CompressFormat.PNG,0,baos);
                        byteArray[] bArray = baos.toByteArray();
    If you want you can get the name of image as:
     try (Cursor returnCursor = getContentResolver().query(myUri, null, null, null, null)) {
                        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                        returnCursor.moveToFirst();
                           String FileName = returnCursor.getString(nameIndex);}
person Deepya    schedule 01.11.2018