Android onPictureTaken 导致图像旋转不佳 quality/wrong

Android onPictureTaken resulting in poor quality/wrong rotated images

我在应用程序中保存图像时遇到问题。我在 android 版本 4.4.4 上使用 Android 相机 api 1(api 21 之前)。设备是 OnePlus One。

当我拍照时,我的应用程序中的内置相机似乎保存的图像质量很差,而且还逆时针旋转了 90 度 (-90)。

这是一个图片示例。

使用默认 android 相机应用程序的纵向视图(保存的图像):

使用内置应用程序相机的纵向视图:

使用内置应用程序相机保存时的图片(保存的图像):

第一个问题,旋转方向

现在我猜旋转是由于这个原因造成的(如果我不更改 setDisplayOrientation,相机在我的应用程序中会倾斜):

public void refreshCamera(Camera camera) {
    if (holder.getSurface() == null) {
        // preview surface does not exist
        return;
    }
    // stop preview before making changes
    try {
        camera.stopPreview();
    } catch (Exception e) {
        // ignore: tried to stop a non-existent preview
    }

    int rotation = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
    int degrees = 0;

    // specifically for back facing camera
    switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 90;
            break;
        case Surface.ROTATION_90:
            degrees = 0;
            break;
        case Surface.ROTATION_180:
            degrees = 270;
            break;
        case Surface.ROTATION_270:
            degrees = 180;
            break;
    }

    camera.setDisplayOrientation(degrees);
    setCamera(camera);
    try {
        camera.setPreviewDisplay(holder);
        camera.startPreview();
    } catch (Exception e) {
        Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
    }
}

为了解决这个问题,我想我可以在保存图像后旋转图像,不过编写这样的方法似乎是浪费代码。

第二个问题,质量

这个我也不知道为什么画质这么差,我猜是跟这个有关:

private PictureCallback getPictureCallback() {
    return new PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            // save picture on seperate thread so camera can refresh quicker
            new Thread(new SavePicThread(data)).start();
            // refresh camera to continue preview
            cameraPreview.refreshCamera(camera);
        }
    };
}

public class SavePicThread implements Runnable {
    byte[] data;
    public SavePicThread(byte[] data) {
        this.data = data;
    }
    public void run() {
        // make a new picture file
        File pictureFile = getOutputMediaFile();

        if (pictureFile == null) {
            return;
        }
        try {
            // write to the file
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.flush();
            fos.close();
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast toast = Toast.makeText(getActivity(), "Picture saved", Toast.LENGTH_SHORT);
                    toast.show();
                }
            });
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // make the picture visible to the rest of the device
        galleryAddPic(pictureFile);
    }
}

// make picture and save to a folder
private File getOutputMediaFile() {
    // make a new file directory inside the "sdcard" folder
    // File mediaStorageDir = new File("/sdcard/", "fela"); // private pic for app

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "fela");

    // if the directory does not exist
    if (!mediaStorageDir.exists()) {
        // if you cannot make this directory return
        if (!mediaStorageDir.mkdirs()) {
            return null;
        }
    }

    // take the current timeStamp
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    // and make a media file:
    mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");

    return mediaFile;
}

/**
 * makes the image visible for the device (gallery)
 * @param pic file
 */
private void galleryAddPic(File file) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri contentUri = Uri.fromFile(file);
    mediaScanIntent.setData(contentUri);
    getActivity().sendBroadcast(mediaScanIntent);
}

一直在看教程,这就是他们涵盖的内容!

我了解到您找到了图像质量的解决方案。实际上,您不能假设默认的 PictureSize 是设备上可用的最佳质量;你应该使用 getSupportedPictureSizes() 然后调用 setPictureSize().

关于轮换,情况比较麻烦。相机参数支持 setRotation() 方式

Sets the clockwise rotation angle in degrees relative to the orientation of the camera. This affects the pictures returned from JPEG Camera.PictureCallback. The camera driver may set orientation in the EXIF header without rotating the picture. Or the driver may rotate the picture and the EXIF thumbnail. If the Jpeg picture is rotated, the orientation in the EXIF header will be missing or 1 (row #0 is top and column #0 is left side).

从描述中可以看出,有些设备实际上会保存方向正确的 JPEG 图片,但其他设备只会保存 EXIF header。

不幸的是,方向标志对于许多图像查看器应用程序来说是不够的,例如 - 对于 Windows built-in 图像预览。以编程方式执行 full-size 图像的旋转是可能的,但这是一项时间和(更重要的)memory-consuming 任务。最简单的方法是 create a bitmap and rotate it.

许多人使用缩放位图来加快旋转速度并使用更少的内存。但这将不可避免地破坏您获得最佳画质的目的。