![](/img/trans.png)
[英]Add text to the full-size picture saved by the Android Camera Application and save as a new file without losing quality
[英]How to rotate a JPEG file on Android without losing quality and gaining file size?
我需要旋转相机拍摄的图像,以便它们始终具有正常方向。
为此,我使用下一个代码(使用此帖子来获取图像方向)
//<= get the angle of the image , and decode the image from the file
final Matrix matrix = new Matrix();
//<= prepare the matrix based on the EXIF data (based on https://gist.github.com/9re/1990019 )
final Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix,false);
bitmap.recycle();
fileOutputStream = new FileOutputStream(tempFilePath);
rotatedBitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);
rotatedBitmap.recycle();
这里压缩率(AKA“质量”参数)是100。
代码工作正常,但结果比原来大,大得多。
原始文件大约为600-700 KB,而生成的文件大约为3MB ...
即使输入文件和输出文件都具有相同的格式(JPEG),这也是如此。
相机设置为“超精细”质量。 不确定它意味着什么,但我认为这与压缩率有关。
我试图将“filter”参数设置为false或true。 两者都导致了大文件。
即使没有旋转本身(只是解码和编码),我也会获得更大的文件大小......
只有当我将压缩比设置为大约85时,我才会得到类似的文件大小,但我想知道与原始文件相比质量会受到什么影响。
它为什么会发生?
有没有办法获得完全相同的输入文件的大小和质量?
使用与原始文件相同的压缩率会使它发生吗? 甚至可以获得原始文件的压缩率?
拥有100%压缩率意味着什么?
编辑:我发现这个链接谈论JPEG文件的旋转而不会丢失质量和文件大小,但在Android上是否有解决方案?
这是另一个链接 ,说它是可能的,但我找不到任何允许旋转jpeg文件而不会失去其质量的库
我尝试了两种方法,但我发现这些方法在我的情况下花了太长时间。 我仍然分享我用过的东西。
方法1:适用于Android的LLJTran
从这里获取LLJTran: https : //github.com/bkhall/AndroidMediaUtil
代码:
public static boolean rotateJpegFileBaseOnExifWithLLJTran(File imageFile, File outFile){
try {
int operation = 0;
int degree = getExifRotateDegree(imageFile.getAbsolutePath());
//int degree = 90;
switch(degree){
case 90:operation = LLJTran.ROT_90;break;
case 180:operation = LLJTran.ROT_180;break;
case 270:operation = LLJTran.ROT_270;break;
}
if (operation == 0){
Log.d(TAG, "Image orientation is already correct");
return false;
}
OutputStream output = null;
LLJTran llj = null;
try {
// Transform image
llj = new LLJTran(imageFile);
llj.read(LLJTran.READ_ALL, false); //don't know why setting second param to true will throw exception...
llj.transform(operation, LLJTran.OPT_DEFAULTS
| LLJTran.OPT_XFORM_ORIENTATION);
// write out file
output = new BufferedOutputStream(new FileOutputStream(outFile));
llj.save(output, LLJTran.OPT_WRITE_ALL);
return true;
} catch(Exception e){
e.printStackTrace();
return false;
}finally {
if(output != null)output.close();
if(llj != null)llj.freeMemory();
}
} catch (Exception e) {
// Unable to rotate image based on EXIF data
e.printStackTrace();
return false;
}
}
public static int getExifRotateDegree(String imagePath){
try {
ExifInterface exif;
exif = new ExifInterface(imagePath);
String orientstring = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientstring != null ? Integer.parseInt(orientstring) : ExifInterface.ORIENTATION_NORMAL;
if(orientation == ExifInterface.ORIENTATION_ROTATE_90)
return 90;
if(orientation == ExifInterface.ORIENTATION_ROTATE_180)
return 180;
if(orientation == ExifInterface.ORIENTATION_ROTATE_270)
return 270;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
方法2:使用libjepg-turbo的jpegtran可执行文件
1按照此处描述的步骤操作: https : //stackoverflow.com/a/12296343/1099884
除了你不需要在ndk-build
上使用obj/local/armeabi/libjpeg.a
,因为我只想要jpegtran可执行文件但不要使用libjepg.a
JNI。
2将jpegtran可执行文件放在资产文件夹中。 代码:
public static boolean rotateJpegFileBaseOnExifWithJpegTran(Context context, File imageFile, File outFile){
try {
int operation = 0;
int degree = getExifRotateDegree(imageFile.getAbsolutePath());
//int degree = 90;
String exe = prepareJpegTranExe(context);
//chmod ,otherwise premission denied
boolean ret = runCommand("chmod 777 "+exe);
if(ret == false){
Log.d(TAG, "chmod jpegTran failed");
return false;
}
//rotate the jpeg with jpegtran
ret = runCommand(exe+
" -rotate "+degree+" -outfile "+outFile.getAbsolutePath()+" "+imageFile.getAbsolutePath());
return ret;
} catch (Exception e) {
// Unable to rotate image based on EXIF data
e.printStackTrace();
return false;
}
}
public static String prepareJpegTranExe(Context context){
File exeDir = context.getDir("JpegTran", 0);
File exe = new File(exeDir, "jpegtran");
if(!exe.exists()){
try {
InputStream is = context.getAssets().open("jpegtran");
FileOutputStream os = new FileOutputStream(exe);
int bufferSize = 16384;
byte[] buffer = new byte[bufferSize];
int count;
while ((count=is.read(buffer, 0, bufferSize))!=-1) {
os.write(buffer, 0, count);
}
is.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return exe.getAbsolutePath();
}
public static boolean runCommand(String cmd){
try{
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
}
reader.close();
// Waits for the command to finish.
process.waitFor();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
不幸的是,两者都耗时太长。 在我的三星Galaxy S1上是16秒!!!! 但我发现这个应用程序( https://play.google.com/store/apps/details?id=com.lunohod.jpegtool )只需3-4秒。 必须有一些方法可以做。
一旦你完成设置bestPreviewSize你现在必须设置bestPictureSize每个手机支持不同的图片大小,以获得最佳图片质量,你必须检查支持的图片大小,然后设置最佳大小的相机参数。 您必须在曲面中设置这些参数以获得宽度和高度。 将在启动时调用surfaceChanged,从而设置新参数。
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters myParameters = camera.getParameters();
myPicSize = getBestPictureSize(width, height);
if (myBestSize != null && myPicSize != null) {
myParameters.setPictureSize(myPicSize.width, myPicSize.height);
myParameters.setJpegQuality(100);
camera.setParameters(myParameters);
Toast.makeText(getApplicationContext(),
"CHANGED:Best PICTURE SIZE:\n" +
String.valueOf(myPicSize.width) + " ::: " + String.valueOf(myPicSize.height),
Toast.LENGTH_LONG).show();
}
}
现在getBestPictureSize ..
private Camera.Size getBestPictureSize(int width, int height)
{
Camera.Size result=null;
Camera.Parameters p = camera.getParameters();
for (Camera.Size size : p.getSupportedPictureSizes()) {
if (size.width>width || size.height>height) {
if (result==null) {
result=size;
} else {
int resultArea=result.width*result.height;
int newArea=size.width*size.height;
if (newArea>resultArea) {
result=size;
}
}
}
}
return result;
}
为了轮换,试试这个..
final Matrix matrix = new Matrix();
matrix.setRotate(90):
final Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix,false);
我正在使用PNG图像,它工作正常....对于JPEG图像,请检查上面的代码。
100%质量率可能是比最初保存文件的设置更高的质量设置。 这导致更大的尺寸但(几乎)相同的图像。
我不确定如何获得完全相同的尺寸,也许只是将质量设置为85%(快速和脏)。
但是,如果您只想以90°步进旋转图片,则可以仅编辑JPEG元数据而无需触摸像素数据本身。
不确定它是如何在android中完成的,但这就是它的工作原理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.