图像质量帮助 - Android

时间:2014-01-21 14:42:52

标签: android image photo

我有一个照片编辑应用。它只是将图像添加到基础照片上。输出照片的质量低于相机拍摄的原始照片。这是预期的,但也许我可以改进我现在拥有的东西。这是500px和1000px相同的质量,这非常令人担忧......我可以看到我在像素以外的地方限制质量。我相机库中的原始照片是JPG文件。以下是我为获取照片,从中创建位图,然后保存它所做的一切。你能告诉我代码在哪里可以降低照片质量吗?

打开图库意图:

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

1 个答案:

答案 0 :(得分:1)

您在此处看到的问题肯定是由JPEG压缩工件引起的。您可以通过放大时可以看到的特征8x8像素块来看到这一点。

JPEG很棘手。它们具有非常好的压缩比,但是它们使用这些8x8块进行压缩(技术:8x8块是用于内容编码的量化离散余弦变换的区域)。打开并再次保存JPEG时,会重新压缩这些块。

通常,如果多次打开,保存和关闭相同的图像,图像质量就不会有巨大的差异。这是因为相同的块被编码和量化相同,因此丢失的信息已经丢失。

但是,如果裁剪JPEG图像以使8x8伪像不再与所使用的8x8压缩网格对齐,那么事情就会变得非常混乱。 JPEG压缩器不会理解它应该真正压缩移动了几个像素的网格,并且会尝试将每个旧块的碎片混合成新块。在这种情况下,有很多信息再次被量化,你会得到一些非常显着的降级。

使用JPEG时(大多数情况下)避免此伪像的最简单方法是在裁剪时强制与8x8图像网格对齐。但是,如果您真的想要进行无损质量裁剪,则必须在JFIF包装器内操作未解压缩的DCT块。这很难看,但可行。但是,我不知道这个Android的实现。

外卖:在裁剪和重新压缩时,JPEG很挑剔 - 尝试强制与8x8网格对齐以减少质量损失。