Android 相机预览在预览中拉伸,而不是在拍照后

Posted

技术标签:

【中文标题】Android 相机预览在预览中拉伸,而不是在拍照后【英文标题】:Android Camera Preview Stretched in Preview, Not After Picture Taken 【发布时间】:2016-02-20 14:15:56 【问题描述】:

我有一个以纵向模式拍照的应用程序,一切正常,只是预览的大小与拍照后的图像大小不同。预览看起来失真和模糊,但会填满整个屏幕。拍照后,图像清晰,但要么比设备高度短,要么宽度更小(取决于正在使用的设备)。

以下是我正在使用的一些图像示例和一些代码。

拍照前:

拍照后:

要纵向显示的相机预览代码:

protected int getRotationDegrees() 
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(0, info);
    int rotation = ((WindowManager) mContext
            .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
            .getRotation();
    int degrees = 0;

    switch (rotation) 
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
    

    int result = (info.orientation - degrees + 360) % 360;
    return result;

拍照后旋转图像的代码:

Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() 

    @Override
    public void onPictureTaken(byte[] data, Camera camera) 
        Bitmap pictureBitmap = null;

        pictureBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

        int rotationDegrees = mCameraPreview.getRotationDegrees();
        //mRotation = setCameraDisplayOrientation(MainActivity.this, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
        Matrix matrix = new Matrix();
        matrix.postRotate(rotationDegrees);

        //Bitmap scaledBitmap = Bitmap.createScaledBitmap(pictureBitmap ,previewWidth ,previewHeight ,true);
        //Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
        Bitmap rotatedBitmap = Bitmap.createBitmap(pictureBitmap , 0, 0, pictureBitmap.getWidth(), pictureBitmap.getHeight(), matrix, true);

        //rotatedBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        ImageView previewImage = (ImageView) findViewById(R.id.preview_image);
        previewImage.setImageBitmap(rotatedBitmap);
        mCameraPreview.setVisibility(View.INVISIBLE);
        mButtonCapture.setVisibility(View.INVISIBLE);
        //flCameraPreview.setVisibility(View.INVISIBLE);
    
;

以前有人遇到过这个问题吗?我仍在尝试了解相机 API。提前致谢!

编辑:我用来选择预览和图片尺寸的代码。

private Camera.Size getOptimalSize(List<Camera.Size> sizes, int h, int w) 
    final double ASPECT_TOLERANCE = 0.05;
    double targetRatio = (double) w/h;

    if (sizes == null) 
        return null;
    

    Camera.Size optimalSize = null;

     double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    for (Camera.Size size : sizes) 
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
        if (Math.abs(size.height - targetHeight) < minDiff) 
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        
    

    if (optimalSize == null) 
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) 
            if (Math.abs(size.height - targetHeight) < minDiff) 
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            
        
    

    return optimalSize;

【问题讨论】:

【参考方案1】:

我在另一个帖子中发现了这个comment,解决了我的问题!

它会更改视图的大小,以便图像在始终试图填满屏幕时不会被拉伸。

【讨论】:

【参考方案2】:

这种情况很典型。预览帧的纵横比可能与捕获的图像不完全相同。您可能会遍历支持的预览尺寸以选择最适合您的 SurfaceView 的尺寸(我希望您正确执行此操作)。您需要通过支持的图片大小进行类似的迭代。如果您找不到满意的长宽比相同的对,则需要在视觉上“裁剪” SurfaceView 或 ImageView。实现这种裁剪的最简单方法是用其他一些不透明的视图覆盖视图中不需要的部分。

也可以强制视图(SurfaceView 或 ImageView)“挂出”,部分地在屏幕之外。

【讨论】:

你有任何将图像悬挂在边缘的例子吗?我不认为裁剪视图对我有用,因为预览图像看起来与拍摄的照片具有不同的纵横比(预览中的对象看起来更短且矮)。我在原始帖子中添加了用于选择尺寸的代码。另外,我将预览设置为 FrameLayout,而不是 SurfaceView 或 ImageView。这是否正确且有效,因为我的预览类扩展了 SurfaceView? 我没有看到您的项目,但我猜您将派生自 SurfaceView 的视图添加到 FrameLayout。这使得“闲逛”变得非常容易:首先选择预览大小,然后将此视图添加到框架中,并带有负边距。 ImageView 也是如此。我还建议 setPictureSize() 以最适合支持的大小。 太好了,谢谢。它只是猜测和检查,然后硬编码视图上的负边距吗?或者我可以使用我为 setPreviewSize 和 setPictureSize 选择的尺寸以编程方式执行此操作吗? 不用猜,你可以知道 FrameLayout 的大小,你应该知道预览和图片的选择大小。请记住,Android 会为您进行缩小,因此对于 1920x1080 预览,如果您的 SurfaceView 在应用负边距后为 1280x720 是完全可以的。

以上是关于Android 相机预览在预览中拉伸,而不是在拍照后的主要内容,如果未能解决你的问题,请参考以下文章

即使设置了正确的尺寸,相机预览也会被拉伸

在android中将相机预览拉伸到全屏

2960x1440 拉伸 Android 相机预览

Android 自定义相机 Camera 预览变形拉伸问题

Android:访问硬件相机预览帧数据而不绘制它们

如何在一帧后面显示预览相机并拍照包括帧