Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/204.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Android上旋转JPEG文件而不丢失质量和增加文件大小? 背景_Android_Image_Jpeg_Image Compression - Fatal编程技术网

如何在Android上旋转JPEG文件而不丢失质量和增加文件大小? 背景

如何在Android上旋转JPEG文件而不丢失质量和增加文件大小? 背景,android,image,jpeg,image-compression,Android,Image,Jpeg,Image Compression,我需要旋转相机拍摄的图像,以便它们始终具有法线方向 为此,我使用下一个代码(用于获取图像方向) /100%质量比率可能是比文件最初保存时使用的设置更高的质量设置。这将导致更大的尺寸,但(几乎)相同的图像 我不知道如何得到完全相同的大小,也许只是将质量设置为85%就可以了(快速和肮脏) 但是,如果您只想将pic旋转90°,您可以只编辑JPEG元数据而不接触像素数据本身 不确定android是如何实现的,但这就是它的工作原理。对于旋转,请尝试此 final Matrix matrix = new M

我需要旋转相机拍摄的图像,以便它们始终具有法线方向

为此,我使用下一个代码(用于获取图像方向)


/100%质量比率可能是比文件最初保存时使用的设置更高的质量设置。这将导致更大的尺寸,但(几乎)相同的图像

我不知道如何得到完全相同的大小,也许只是将质量设置为85%就可以了(快速和肮脏)

但是,如果您只想将pic旋转90°,您可以只编辑JPEG元数据而不接触像素数据本身


不确定android是如何实现的,但这就是它的工作原理。

对于旋转,请尝试此

final Matrix matrix = new Matrix(); 
matrix.setRotate(90): 
final Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix,false);

我使用的是PNG图像,效果很好。。。。对于JPEG图像,请检查上面的代码。

我尝试了两种方法,但我发现这些方法在我的情况下花费的时间太长。我仍然分享我用过的东西

方法1:LLJTran for Android

从这里获取LLJTran:

守则:

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;
}
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;
    }
}
方法2:使用libjepg-turbo的jpegtran可执行文件

1按照此处描述的步骤进行操作:

除了在
ndk build
上不需要
obj/local/armeabi/libjpeg.a
,因为我只需要jpegtran可执行文件,而不需要将JNI与
libjepg.a
混为一谈

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秒!!!!但我发现这个应用()只需要3-4秒。一定有办法。

设置好最佳预览大小后,现在必须设置为最佳图片大小每个手机都支持不同的图片大小,因此要获得最佳图片质量,您必须检查支持的图片大小,然后将“最佳大小”设为“照相机”参数。您必须在“曲面更改”中设置这些参数以获得宽度和高度。SURFACHECHANGED将在start中调用,因此将设置新参数

 @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;

} 


jpeg不是无损的。它总是有损耗的。关于编辑元数据,它不会有帮助,因为我希望所有图像查看器都能正确地查看图像,而相机应用程序有时会将方向添加到元数据中,因此,如果您显示图像,它将与用户所做的不一样。例如,如果以90度CW拍摄图像,并且用户打开图像,则拍摄图像数据时不考虑方向,因此将旋转图像数据,元数据将仅保持文件的方向。一个好的图像查看器会考虑它并通过它自己旋转。这里有一个关于JPEG的神话页面:它包括你写的关于100%质量率的东西。设置方向标签是你的最佳选择。我见过的唯一忽略方向标记的图像查看器是“Windows图像查看器”(内置应用)的旧版本。我的C图像库允许无损旋转,但它不是免费的,需要调用本机代码。旧版本的“Windows图像查看器”?所以windows 8很旧了…:)无论如何,我希望避免假设人们如何显示图像。我使用的是PNG图像,它工作正常。。。。对于JPEG,请检查上面的代码。@androidDev PNG可以正常工作,因为它们是无损的。我问过JPEG文件,它们是有损的。你解决了吗?我尝试了LLJTran,但它在读取图像文件时抛出异常。上面的代码是什么?另外,我的问题是关于JPEG,因为它们是有损的(压缩时与原始图像相比会丢失质量),以及如何在不丢失原始JPEG文件质量的情况下从中生成文件。您的代码与我所问的任何问题都没有任何关系。@416E64726577无论如何,这不是一个真正的答案。它再次对文件进行解码和编码,这是我特别写的,我不想要的,因为它会因为重新压缩而失去质量。你检查了输出位图了吗?它们是否与原始位图的旋转版本相同?@android developer,我保存了原始jpeg文件,并将文件旋转了90度4次(使用jepgtran)连接到PC并使用diffimg进行检查。它显示没有像素不同。酷。顺便说一句,你不必使用电脑,你只需将位图存储在设备上并比较像素。。。这个答案看起来很有希望。我希望我能有机会去看看。现在,这是一个向上投票(+1)。:)你链接到的应用程序不存在。您是否成功地优化了代码和配置?你能把它分享到Github吗?另外,你能为所有类型的方向解出它吗?其中一些需要翻转图像…问题是旋转JPEG文件而不丢失质量,但这也很好。再次检查旋转图像@androiddeveloper,了解相机是件好事,但问题是关于图像文件(可以通过相机创建)。如果将此旋转设置为“照相机”,则在保存图像文件时,图像文件将自动旋转。您不必为图像数据创建位图,也不必对位图应用其他旋转方法。用户始终可以使用第三方照相机应用程序。我想处理camera应用程序生成的图像文件,它有自己的元数据(EXIF)来描述图像是如何被捕获的(oritentation等)。