SurfaceView上的takePicture导致小图像文件和错误的方向

时间:2014-09-23 19:56:06

标签: android android-camera surfaceview

我有一个活动作为FrameLayout布局,作为片段的容器视图。这个片段包含一个SurfaceView,我在其SurfaceHolder中显示相机预览。

使用PhotoHandler回调调用相机的SurfaceView takePicture点击/点击,实现PictureCallback。重写的方法onPictureTaken接收包含图像数据的字节数组,它使用FileOutputStream存储。

我将活动限制为纵向(在清单中),并使用方法setCameraDisplayOrientation(在stackoverflow上找到),它也会以纵向显示相机预览。

一切都很顺利......除了两件事:

  1. 生成的图像文件非常小(大约40KB)
  2. 生成的图像文件似乎向左旋转了90度
  3. 所以我的问题是

    我可以提高图像质量(全尺寸)吗?

    我可以确保生成的图像文件有纵向吗?

    以下是我的一些代码

    的AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="org.xxxxxxxxxxxxxxx" >
    
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
        <uses-feature android:name="android.hardware.camera" />
        <uses-feature android:name="android.hardware.camera.autofocus" />
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".xxxxxxxxxx"
                android:label="@string/app_name"
                android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    onPictureTaken

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
    
        File pictureFileDir = getDir();
    
        if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
    
            Toast.makeText(context, "Can't create directory to save image.",
                    Toast.LENGTH_LONG).show();
            return;
    
        }
    
        String filename = pictureFileDir.getPath() + File.separator + photoFile;
    
        File pictureFile = new File(filename);
    
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (Exception error) {
            Toast.makeText(context, "Image could not be saved.",
                    Toast.LENGTH_LONG).show();
        }
    }
    

    如果您还有其他需要,请告诉我......

    编辑:我找到了尺寸问题的解决方案(见下面的答案)。但我仍然有旋转结果图像文件的问题。经过大量测试后,我得到的印象是,即使以纵向模式拍摄,图像也始终以横向格式存储。也许文件中的一些EXIF信息将其标记为肖像......我一直在测试,但您仍然可以在这里帮助我: - )

    EDIT2:关于Exif信息的想法似乎是正确的起点。标准方向(至少在我手机的相机上)似乎是“风景向左旋转90度”。至少,这是图像文件的Exif信息告诉我的内容:图像的第0行位于顶部,第0列位于左侧(TAG_ORIENTATION为1)

    即使我明确地以纵向模式拍摄照片,Exif信息也没有反映出来。它似乎“认为”图像处于横向模式,所以我必须向右旋转90度才能显示它。这似乎对我来说是错误的...我怎样才能确保图像中的Exif信息反映出相机/应用程序的方向? (TAG_ORIENTATION显然应该是6)

    不要误会我的意思:我仍然需要向右旋转90度,但是Exif信息的TAG_ORIENT会告诉我这样做。目前,我必须依赖于我对照片拍摄方式的了解。

    请参阅: Android: Bitmaps loaded from gallery are rotated in ImageView

    和: http://sylvana.net/jpegcrop/exif_orientation.html

    EDIT3:解决方案......见下文

1 个答案:

答案 0 :(得分:4)

好的,我能够至少解决尺寸问题。我发现了PictureSize参数,我可以根据手机/相机的功能设置所需的最终图像尺寸

Camera.Parameters params = camera.getParameters();

List<Camera.Size> sizeList = params.getSupportedPictureSizes();
int chosenSize = Helper.getPictureSizeIndexForHeight(sizeList, 800);
params.setPictureSize(sizeList.get(chosenSize).width, sizeList.get(chosenSize).height);

camera.setParameters(params);

和Helper方法帮助我选择最小高度的图片大小比第二个参数(或最大可用大小)更好

public static int getPictureSizeIndexForHeight(List<Camera.Size> sizeList, int height) {
    int chosenHeight = -1;
    for(int i=0; i<sizeList.size(); i++) {
        if(sizeList.get(i).height < height) {
            chosenHeight = i-1;
            if(chosenHeight==-1)
                chosenHeight = 0;
            break;
        }
    }
    return chosenHeight;
}

这样可行,但我有旋转图像结果文件的问题

编辑:我发现我需要设置另一个名为Rotation的参数,具体取决于手机方向和UI方向。在这种情况下,手机将设置正确的exif信息,以便在以肖像方式拍摄时图像不会以横向显示。我不再需要旋转,图像也会在相册中正确显示。

public static void setRotationParameter(Activity activity, int cameraId, Camera.Parameters param) {

    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);

    int rotation = activity.getWindowManager().getDefaultDisplay()
            .getRotation();
    rotation = (rotation + 45) / 90 * 90;

    int toRotate = (info.orientation + rotation) % 360;

    param.setRotation(toRotate);
}