我有一个照片编辑应用。它只是将图像添加到基础照片上。输出照片的质量低于相机拍摄的原始照片。这是预期的,但也许我可以改进我现在拥有的东西。这是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();
}
答案 0 :(得分:1)
您在此处看到的问题肯定是由JPEG压缩工件引起的。您可以通过放大时可以看到的特征8x8像素块来看到这一点。
JPEG很棘手。它们具有非常好的压缩比,但是它们使用这些8x8块进行压缩(技术:8x8块是用于内容编码的量化离散余弦变换的区域)。打开并再次保存JPEG时,会重新压缩这些块。
通常,如果多次打开,保存和关闭相同的图像,图像质量就不会有巨大的差异。这是因为相同的块被编码和量化相同,因此丢失的信息已经丢失。
但是,如果裁剪JPEG图像以使8x8伪像不再与所使用的8x8压缩网格对齐,那么事情就会变得非常混乱。 JPEG压缩器不会理解它应该真正压缩移动了几个像素的网格,并且会尝试将每个旧块的碎片混合成新块。在这种情况下,有很多信息再次被量化,你会得到一些非常显着的降级。
使用JPEG时(大多数情况下)避免此伪像的最简单方法是在裁剪时强制与8x8图像网格对齐。但是,如果您真的想要进行无损质量裁剪,则必须在JFIF包装器内操作未解压缩的DCT块。这很难看,但可行。但是,我不知道这个Android的实现。
外卖:在裁剪和重新压缩时,JPEG很挑剔 - 尝试强制与8x8网格对齐以减少质量损失。