2014-01-21 3 views
0

У меня есть приложение для редактирования фотографий. Все, что он делает, это добавить изображения на базовую фотографию. Выходная фотография более низкое, чем исходная фотография, которую снимает камера. Это ожидается, но, возможно, я смогу улучшить то, что у меня есть. Это то же качество в 500px, что и 1000px, что очень важно ... Я вижу, что я ограничиваю качество где-то, кроме пикселей. Исходные фотографии в галерее камеры - файлы JPG. Вот все, что я делаю, чтобы получить фотографию, создать растровое изображение и сохранить ее. Можете ли вы рассказать мне, где в коде качество фотографии может снизиться?Качество изображения - Android

открытая галерея Цель:

Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); 
     photoPickerIntent.setType("image/*"); 
     startActivityForResult(photoPickerIntent, 1); 

OnActivityResult() для выбранной фотографии:

if (intent != null && resultcode == RESULT_OK) 
          { 
           mProfilePicPath = ih.getSelectedImageFilePathFromGallery(this.getApplicationContext(), intent); 
           mPortraitPhoto = ih.decodeSampledBitmapFromImagePath(mProfilePicPath, 
             GlobalConstants.PROFILE_PICTURE_RESOLUTION, 
             GlobalConstants.PROFILE_PICTURE_RESOLUTION); 
          } 

     public String getSelectedImageFilePathFromGallery(Context ctx, 
        Intent intent) { 
       Uri selectedImage = intent.getData(); 
       String[] filePathColumn = {MediaStore.Images.Media.DATA}; 
       Cursor cursor = ctx.getContentResolver().query(selectedImage, filePathColumn, null, null, null); 
       cursor.moveToFirst(); 
       int columnIndex = cursor.getColumnIndex(filePathColumn[0]); 
       String filePath = cursor.getString(columnIndex); 
       cursor.close(); 
       return filePath; 
      } 

    public Bitmap decodeSampledBitmapFromImagePath(String imagePath, int reqWidth, int reqHeight) { 
      // First decode with inJustDecodeBounds=true to check dimensions 
      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inJustDecodeBounds = true; 
      BitmapFactory.decodeFile(imagePath, options); 

      int imageHeight = options.outHeight; 
      int imageWidth = options.outWidth; 
      String imageType = options.outMimeType; 
      Log.d("Image dims", imageType + ", " + imageHeight + ", " + imageWidth + "size."); 

      // Calculate inSampleSize 
      options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 

      // Decode bitmap with inSampleSize set 
      options.inJustDecodeBounds = false; 
      Bitmap portraitPhoto = ImageHelper.convertToPortraitOrientation(options, imagePath); 
      return portraitPhoto; 

     } 

public static int calculateInSampleSize(
      BitmapFactory.Options options, int reqWidth, int reqHeight) { 
    // Raw height and width of image 
    final int height = options.outHeight; 
    final int width = options.outWidth; 
    int inSampleSize = 1; 

    if (height > reqHeight || width > reqWidth) { 

     final int halfHeight = height/2; 
     final int halfWidth = width/2; 

     // Calculate the largest inSampleSize value that is a power of 2 and keeps both 
     // height and width larger than the requested height and width. 
     while ((halfHeight/inSampleSize) > reqHeight 
       && (halfWidth/inSampleSize) > reqWidth) { 
      inSampleSize *= 2; 
     } 
    } 
    return inSampleSize; 
} 

    public static Bitmap convertToPortraitOrientation(BitmapFactory.Options options, String path) { 
      Uri actualUri = Uri.parse(path); 
      float degree = 0; 

      Bitmap bmp = BitmapFactory.decodeFile(path, options); 
      try { 
       ExifInterface exif = new ExifInterface(actualUri.getPath()); 
       String exifOrientation = exif 
         .getAttribute(ExifInterface.TAG_ORIENTATION); 

       if (bmp != null) { 
        degree = getDegree(exifOrientation); 
        if (degree != 0) 
         bmp = createRotatedBitmap(bmp, degree); 
       } 
      } 
      catch (FileNotFoundException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      return bmp; 
     } 

    public static Bitmap createRotatedBitmap(Bitmap bm, float degree) { 
      Bitmap bitmap = null; 
      if (degree != 0) { 
       Matrix matrix = new Matrix(); 
       matrix.preRotate(degree); 
       bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), 
         bm.getHeight(), matrix, true); 
      } 
      return bitmap; 
     } 

    public File createProfilePicSaveFileInternal(Context ctx) { 

      //mBackgroundImage.setImageBitmap(ih.decodeSampledBitmapFromImagePath(mProfilePicPath, 500, 500)); 


      String path = ctx.getFilesDir() + File.separator + "My Folder"; 
      File outputDir= new File(path); 
      outputDir.mkdirs(); 
      File newFile = new File(path + "/" + mName + ".jpg"); 

      return newFile; 
     } 

    @TargetApi(Build.VERSION_CODES.GINGERBREAD) 
     public void saveImage(Bitmap bitmap, Context ctx, File newFile) { 
      FileOutputStream fos; 
      newFile.setReadable(true, false); 
      try { 
       fos = new FileOutputStream(newFile); 
       bitmap.compress(CompressFormat.JPEG, 100, fos); 
       fos.flush(); 
       fos.close(); 
      } catch (FileNotFoundException e) { 
       Log.e("GREC", e.getMessage(), e); 
      } catch (IOException e) { 
       Log.e("GREC", e.getMessage(), e); 
      } 
      ctx.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory()))); 

     } 

при нажатии кнопки:

File newInternalFile = ih.createProfilePicSaveFileInternal(this.getApplicationContext()); 
      boolean s = newInternalFile.exists(); 
      long length = newInternalFile.length(); 
      ih.saveImage(mPortraitPhoto, this.getApplicationContext(), newInternalFile); 
      mPortraitPhoto = null; 

public File createProfilePicSaveFileInternal(Context ctx) { 

     //mBackgroundImage.setImageBitmap(ih.decodeSampledBitmapFromImagePath(mProfilePicPath, 500, 500)); 


     String path = ctx.getFilesDir() + File.separator + "My Folder"; 
     File outputDir= new File(path); 
     outputDir.mkdirs(); 
     File newFile = new File(path + "/" + mName + ".jpg"); 

     return newFile; 
    } 

После моего изображения первый сохранен, Я получаю это сохраненное изображение в его истинном виде, делая это (inSampleSize теперь 1):

public Bitmap getPortraitBitmapNotSampled(String imagePath){ 
     BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inJustDecodeBounds = true; 
     BitmapFactory.decodeFile(imagePath, options); 

     int imageHeight = options.outHeight; 
     int imageWidth = options.outWidth; 
     String imageType = options.outMimeType; 
     Log.d("Dressing room photo dims", imageType + ", " + imageHeight + ", " + imageWidth + "size."); 

     // Calculate inSampleSize 
     options.inSampleSize = 1; 

     // Decode bitmap with inSampleSize set 
     options.inJustDecodeBounds = false; 
     Bitmap portraitPhoto = ImageHelper.convertToPortraitOrientation(options, imagePath); 
     return portraitPhoto; 
    } 

Я в конечном итоге принимает снимок экрана, чтобы получить выходное изображение так:

public Bitmap takeScreenshot() { 
     View v = findViewById(R.id.DressBig); 
    v.setDrawingCacheEnabled(true); 
    return v.getDrawingCache(); 
    } 

Затем окончательный сохранить:

private void saveClicked(){ 
     finishedOutfit = takeScreenshot(); 
     File newFile = ih.createOutfitSaveFileExternal(); 
     File newInternalFile = ih.createOutfitSaveFileInternal(this.getApplicationContext()); 
     ih.saveImage(finishedOutfit, this.getApplicationContext(), newFile); 
     ih.saveImage(finishedOutfit, this.getApplicationContext(), newInternalFile); 
     String newFilePath = newInternalFile.toString(); 
     String newExternalFilePath = newFile.toString(); 
     Log.d("db file path: ", newFilePath); 
     Log.d("external file path: ", newExternalFilePath); 
     insertOutfitInDB(newFilePath, newExternalFilePath); 
     showImageSavedDialog(); 
    } 
+0

Есть ли у вас примеры изображений «before» и «after»? Возможность увидеть потерю качества, о котором вы говорите, может дать некоторое представление о том, связано ли это с сжатием JPEG или каким-либо другим источником. – Glenn

+0

@Glenn Спасибо.Сейчас я поставлю эти фотографии сравнения. Как вы думаете, я должен проверить, какой тип файла имеет галерея, и попытаться сохранить этот тип файла? – user3164083

+0

Все зависит от того, чего вы пытаетесь достичь. Если вам нужен однородный набор изображений того же размера и формата, сохранение типа файла является контрпродуктивным. Если вы хотите сохранить одинаковое качество изображения, чтение и запись JPEG таким образом очень опасно, потому что вы повторно сжимаете уже сжатые данные, что никогда не бывает хорошей новостью. – Glenn

ответ

1

Проблема вы видите здесь, безусловно, вызванное JPEG артефакты сжатия. Вы можете увидеть это с помощью характерных 8x8 пиксельных блоков, которые вы можете видеть при масштабировании.

JPEG-файлы сложны. Они имеют очень хорошие коэффициенты сжатия, но они используют эти 8x8 блоков для сжатия (технические: блок 8x8 - это область квантованного дискретного косинусного преобразования, используемого для кодирования содержимого). Когда JPEG открывается и сохраняется снова, эти блоки повторно сжимаются.

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

Однако, если изображение JPEG обрезано так, что артефакты 8x8 больше не выровнены с сеткой сжатия 8x8, тогда все становится очень грязным. Компрессор JPEG не поймет, что он действительно должен сжимать сетку, сдвинутую на несколько пикселей, и попытается смять куски каждого из старых блоков в новые блоки. В этом случае есть много информации, которая снова квантуется, и вы получаете очень значительную деградацию.

Самый простой способ (в основном) избежать этого артефактов при работе с JPEG-файлами - обеспечить выравнивание с сеткой изображений 8x8 при обрезке. Однако, если вы действительно хотите получить обрезку без потерь, вам придется манипулировать блоками DCT без распаковки внутри обертки JFIF. Это уродливо, но выполнимо. Однако я не знаю об этой реализации в Android.

Еда на вынос: JPEG при тонком сжатии и сжатии JPEG - старайтесь выравнивать сетку 8x8, чтобы уменьшить потерю качества.

+0

Спасибо! Я могу переключиться с JPEG, если вы порекомендуете? Как заставить выравнивание с сеткой 8x8? Могу ли я получить отправную точку? Мне действительно не нужна большая трудная задача, и мы будем рассматривать ничего не делать, если единственное исправление - это большое и трудное. У меня есть ограничения по времени. Но я, конечно, хотел бы как-то улучшить его. – user3164083

+0

Если я удалю обрезку, могу ли я тогда ничего не сделать и получить лучшее качество? Это очень возможно для меня. – user3164083

+0

Обратите внимание: я добавляю изображения поверх исходного изображения. Это все редактирование, которое я делаю. Благодаря! – user3164083

Смежные вопросы