全屏相机预览显示不保持宽高比-图像被扭曲,拉伸以适应屏幕。

33

我开发了一个小应用程序,在全屏显示相机预览。我使用的是摄像头 API。

这是活动布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <!-- This is the container for the camera preview screen -->
    <FrameLayout android:id="@+id/camera_preview"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
</LinearLayout>

当设备处于竖屏状态时,为了匹配设备屏幕的高度,显示会进行垂直缩放,因此宽高比与原生相机不同。以下是两张图片更好地说明了我的意思:

enter image description hereenter image description here

第一张图片是使用原生相机设备拍摄的。 第二张图片是使用我的应用程序,并使用全屏相机拍摄的 - 图像被扭曲,拉伸以适应屏幕。

我需要相机预览全屏显示,而不考虑由 getSupportedPreviewSizes() 方法给出的预览大小,并且无失真。有没有办法实现这一点?在相机预览处于全屏状态时保持正确的宽高比?我期望操作系统自动完成这项工作 - 裁剪图像以匹配所请求的分辨率同时保持宽高比,但这并没有发生。

我尝试将 SurfaceView 设为大于显示器的尺寸(按照这个问题:Fitting a camera preview to a SurfaceView larger than the display),但在我的情况下不行,因为我要捕获快照(每秒6帧),这些快照并不是用户在屏幕上看到的内容(即使没有所有相机预览都可见于屏幕)。

我在这里发布了整个项目:https://www.dropbox.com/s/3d52xt8kazynsae/CameraFullScreen.7z?v=0mcn

任何想法/解决方案对我来说都非常重要。非常感谢。

========================================================================

由于ss1271的答案更新:

我将分析一下你在 三星 Galaxy Ace II 上提到的分辨率。

I. 屏幕分辨率: 480x800 - 宽高比为3:5 = 0.6

II. getSupportedPreviewSizes - 我几乎确定这些值来自后置相机。以下是这些分辨率的宽高比:

   2560x1920   - 0,75

   2560x1536   - 0,60

   2048x1536   - 0,75

   2048x1232   - 0,60

   960x720     - 0,75

   640x480     - 0,75

因此,您的方法将返回一个Size,对应于2560x15362048x1232 - 这些具有与屏幕分辨率相同的纵横比,使用这些值不会扭曲图片。对我而言问题在于,由于我每秒捕获6帧,需要以较低的分辨率保存这些帧。
下面是一些来自三星S2设备的结果: I. 屏幕分辨率:480 x 800 - 纵横比3:5 = 0.6 II.后置摄像头 a). getSupportedPreviewSizes:
800 / 480   - 480/800 = 0,60
800 / 450   - 450/800 = 0,56
720 / 480   - 0,66 
640 / 480   - 0,75
352 / 288   - 0,81
320 / 240   - 0,75
176 / 144   - 0,81
b). 本机相机分辨率:
3264 / 2448  - 0,75  - not full screen
3264 / 1968  - 0,60  - FULL SCREEN (since has the same aspect ratio as device screen)
2048 / 1536  - 0,75  - not full screen
2048 / 1232  - 0,60  - FULL SCREEN (same aspect ratio as device screen)
800 / 480    - 0,60  - FULL SCREEN (same aspect ratio as device screen)
640 / 480    - 0, 75 - not full screen

III. 前置摄像头
a). getSupportedPreviewSizes:

640 / 480   - 0,75
352 / 288   - 0,81
320 / 240   - 0,75
176 / 144   - 0,81
b). 本地相机非全屏,无法选择分辨率-选项被禁用。 对于S2后置摄像头而言,我想知道为什么getSupportedPreviewSizes()方法没有返回与本地相机相同的分辨率或本地相机显示的是图片大小?我想知道为什么我没有像getSupportedPreviewSizes()方法中给出的3264 / 1968、2048 / 1232等选项? enter image description here

我想要的是相机预览被裁剪以保持正确的宽高比。 - Paul
我可以问一下吗:您需要定制相机还是只想显示相机取景器(预览)? - dumbfingers
我有一台定制相机,带有叠加按钮。 - Paul
方法getSupportedPreviewSizes()返回预览尺寸,而getSupportedPictureSizes()将返回支持的图片尺寸。根据我的经验,对于您更新的问题,三星Galaxy设备在使用前置摄像头时似乎更有可能拉伸预览图像。但实际上,通过比较长宽比以找到最优尺寸(这就是为什么我在我的回答中有一个getOptimalSize)可以使差异尽可能小。 - dumbfingers
你需要使用与屏幕纵横比(或渲染相机纵横比的视图)相同的预览尺寸。 - Paul
显示剩余2条评论
2个回答

28
简而言之,您可以全屏预览相机画面,但是您需要自己找到适当的尺寸,仅当您需要自定义相机预览时才需要这样做。根据Android Developers -- Camera,如果您要设置相机预览的特定尺寸,请在surfaceChanged()方法中设置此项。在设置预览尺寸时,您必须使用getSupportedPreviewSizes()中的值。请勿在setPreviewSize()方法中设置任意值。似乎您不能手动传递所需的尺寸,除了getSupportedPreviewSizes()提供的尺寸。通过更仔细地检查手机相机支持的尺寸,您会发现支持的尺寸比您的屏幕比例可能不完全相同。例如,三星Galaxy Ace II具有480x800分辨率屏幕,通过阅读从getSupportedPreviewSizes()返回的Size,其相机支持:

2560x1920

2560x1536

2048x1536

2048x1232

960x720

640x480

如果您想正确全屏显示相机预览(无拉伸),则需要计算、比较和应用这些支持的预览尺寸中合适的比例。

找到适当的预览尺寸的实现并不是很复杂。一个常见的方法是这样的:

    /**
     * Calculate the optimal size of camera preview
     * @param sizes
     * @param w
     * @param h
     * @return
     */
    private Size getOptimalSize(List<Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.2;        
        double targetRatio = (double) w / h;         
        if (sizes == null)             
            return null;          
        Size optimalSize = null;         
        double minDiff = Double.MAX_VALUE;          
        int targetHeight = h;          
        // Try to find an size match aspect ratio and size         
        for (Size size : sizes) 
        {             
//          Log.d("CameraActivity", "Checking size " + size.width + "w " + size.height + "h");            
            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);             
            }         
        }          
        // Cannot find the one match the aspect ratio, ignore the requirement     

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

        SharedPreferences previewSizePref;
        if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
            previewSizePref = getSharedPreferences("PREVIEW_PREF",MODE_PRIVATE);
        } else {
            previewSizePref = getSharedPreferences("FRONT_PREVIEW_PREF",MODE_PRIVATE);
        }

        SharedPreferences.Editor prefEditor = previewSizePref.edit();
        prefEditor.putInt("width", optimalSize.width);
        prefEditor.putInt("height", optimalSize.height);

        prefEditor.commit();

//      Log.d("CameraActivity", "Using size: " + optimalSize.width + "w " + optimalSize.height + "h");            
        return optimalSize;     
    }

你也可以类似地找出合适的相机尺寸(输出图片尺寸)。

注意:我从互联网上找到了上述代码的原始版本,并对其进行了一些修改/优化以适应我的个人需求。

请告诉我这是否适用于你。


首先,非常感谢您的回答。我已经更新了问题,以便更好地回应您的答案。我的应用程序正在使用更多的前置摄像头而不是后置摄像头。似乎,如果本机相机由于纵横比问题而无法在全屏显示前置摄像头(至少在S2和S3手机上不行),那么我将无法做到同样的事情而不会扭曲图像... - Paul
非常感谢!不使用setPreviewSize()解决了我的问题。 - BVB
2
如果有人找到这个帖子,我建议你也看一下这里:https://dev59.com/auo6XIcBkEYKwwoYNBdQ - Sam
我正在使用三星S7相机预览,但只显示了屏幕的一半。 - saibaba vali

0

我在使用Camera2 API时遇到了同样的问题,我使用了预览大小和设备宽高比来解决这个问题。你也可以在使用Camera API时采用同样的方法。


你是如何解决这个问题的?使用Camera2 API和TextureView仍然得到了拉伸的预览。 - Erum
不,我能够通过以下解决方案修复纹理视图拉伸问题:https://dev59.com/81oU5IYBdhLWcg3wc2xW#43516672 - Ronak Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接