【问题标题】:Android onPictureTaken resulting in poor quality/wrong rotated imagesAndroid onPictureTaken 导致图像质量差/旋转错误
【发布时间】:2015-07-20 17:22:15
【问题描述】:

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

当我拍照时,我的应用程序中的内置摄像头似乎以低质量保存图像,并且还逆时针旋转了 90 度 (-90)。

这是一个带有图像的示例。

带有默认安卓相机应用的纵向视图(保存的图像):

带有内置应用摄像头的人像视图:

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

第一个问题,旋转方向

现在我猜测的旋转是由于这个(如果我不更改 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);
}

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

【问题讨论】:

  • 1.你用的是什么设备。 2. 您是否将您的图像与 ODM 相机应用程序进行比较? 3、你用的是什么相机参数; developer.android.com/reference/android/hardware/…
  • @MorrisonChang 1. OnePlus One 2. 使用 ODM 我想您的意思是普通的智能手机相机应用程序,是的 3. 我显然没有设置任何...(也许这是问题?从来不知道它存在)
  • 请注意,ODM/普通智能手机应用程序可以访问旧相机 API 中不可用的内容,这可以解释差异。 Camera2 API 的发布旨在帮助创建更好的 3rd 方相机应用程序。有关硬件实现者的详细信息,请参阅source.android.com/devices/camera/camera3.html
  • @MorrisonChang 我用我的应用程序的外观更新了这个问题。 - 所以没有更好的方法来使用旧的 api 拍摄图像吗?这很奇怪,因为我的智能手机(OnePlus One)不支持 api 21,因为它不是棒棒糖。
  • @MorrisonChang 我修好了!我需要将参数setPictureSize 设置为支持的最高宽度/高度,你得到+1 让我走上正轨。 :)

标签: android image camera


【解决方案1】:

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

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

设置相对于相机方向的顺时针旋转角度。这会影响从 JPEG Camera.PictureCallback 返回的图片。相机驱动程序可以在 EXIF 标头中设置方向而不旋转图片。或者驱动程序可以旋转图片和 EXIF 缩略图。如果旋转 Jpeg 图片,EXIF 标头中的方向将丢失或为 1(行#0 为顶部,列#0 为左侧)。

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

不幸的是,方向标志对于许多图像查看器应用程序来说是不够的,例如 - 对于 Windows 内置的图像预览。以编程方式执行全尺寸图像的旋转是可能的,但这是一项耗时且(更重要的是)消耗内存的任务。最简单的方法是create a bitmap and rotate it

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-07
    • 1970-01-01
    • 2012-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    相关资源
    最近更新 更多