在 camera2 apis 中预览延伸

Posted

技术标签:

【中文标题】在 camera2 apis 中预览延伸【英文标题】:preview stretches in camera2 apis 【发布时间】:2018-02-17 10:37:16 【问题描述】:

以下是在 camera2 apis 中使用纹理视图时的屏幕截图。在全屏下,预览会拉伸,但在使用较低分辨率时(第二张图片)可以正常工作。 如何在不拉伸的情况下全屏使用此预览。

【问题讨论】:

您找到解决方案了吗? @akash 直到现在还没有找到解决办法!! @user3819810 这是某种限制吗!你有什么感觉? 可能是,但我不确定。 play.google.com/store/apps/details?id=com.footej.camera 【参考方案1】:

以下答案假设您仅处于纵向模式。

你的问题是

如何在不拉伸的情况下全屏使用预览

让我们把它分解成两件事:

    您希望预览充满整个屏幕 预览不能失真

首先,您需要知道如果您的设备的视口具有不同的纵横比以及相机提供的任何可用分辨率,这在逻辑上是不可能的没有裁剪

所以我假设你接受裁剪预览。

第 1 步:获取可用分辨率列表

StreamConfigurationMap map = mCameraCharacteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) 
    throw new IllegalStateException("Failed to get configuration map: " + mCameraId);

Size[] sizes = map.getOutputSizes(SurfaceTexture.class);

现在您将获得设备相机的可用分辨率(尺寸)列表。

第 2 步:找到最佳纵横比

我们的想法是循环尺寸并查看最适合的尺寸。您可能需要编写自己的“最佳匹配”实现。

我不会在这里提供任何代码,因为我所拥有的与您的用例完全不同。但理想情况下,它应该是这样的:

Size findBestSize (Size[] sizes) 
    //Logic goes here

第 3 步:告诉 Camera API 您要使用此尺寸

    //...
    textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
    Surface surface = textureView.getSurface();
    try 
        mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mPreviewRequestBuilder.addTarget(surface);
        mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                mSessionCallback, null);
     catch (final Exception e) 
        //...
    

第 4 步:让预览超出视口范围

这与 Camera2 API 无关。我们通过让SurfaceView / TextureView 超出设备的视口来“裁剪”预览。

首先将您的SurfaceViewTextureView 放入RelativeLayout

在您从第 2 步获得纵横比后,使用以下内容将其扩展到屏幕之外。请注意,在这种情况下,您可能需要在启动相机之前知道此纵横比。

   //Suppose this value is obtained from Step 2.
    //I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
    float cameraAspectRatio = (float) 0.75;

    //Preparation
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int screenWidth = metrics.widthPixels;
    int screenHeight = metrics.heightPixels;
    int finalWidth = screenWidth;
    int finalHeight = screenHeight;
    int widthDifference = 0;
    int heightDifference = 0;
    float screenAspectRatio = (float) screenWidth / screenHeight;

    //Determines whether we crop width or crop height
    if (screenAspectRatio > cameraAspectRatio)  //Keep width crop height
        finalHeight = (int) (screenWidth / cameraAspectRatio);
        heightDifference = finalHeight - screenHeight;
     else  //Keep height crop width
        finalWidth = (int) (screenHeight * cameraAspectRatio);
        widthDifference = finalWidth - screenWidth;
    

    //Apply the result to the Preview
    RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
    lp.width = finalWidth;
    lp.height = finalHeight;
    //Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
    lp.leftMargin = - (widthDifference / 2);
    lp.topMargin = - (heightDifference / 2);
    cameraView.setLayoutParams(lp);

如果您不关心第 2 步的结果,您实际上可以忽略第 1 步到第 3 步,只需使用那里的库,只要您可以配置其纵横比即可。 (貌似this one是最好的,但我还没试过)

我已经使用my forked library 进行了测试。在不修改我的库的任何代码的情况下,我仅使用第 4 步就设法使预览全屏:

在使用第 4 步之前:

使用第4步后:

拍照后的预览也不会失真,因为预览也超出了您的屏幕。但是输出的图像会包含您在预览中看不到的区域,这使得完美的感觉。

步骤1到步骤3的代码一般引用自Google's CameraView。

【讨论】:

它只是删除了渲染脚本功能。谢谢你,我很抱歉之前的评论 第 4 步解决了问题,甚至没有去 1 到 3 .. 谢谢【参考方案2】:

这是某些设备上的常见问题。我注意到它主要是在三星上。您可以使用技巧在您的 TextureView 上设置转换,使其像 ImageView 行为的 centerCrop

【讨论】:

不,不只是在三星设备上,我还在 Moto 和 Honor 设备上检查过。【参考方案3】:

我也遇到过类似的情况,但是这一行解决了我的问题

view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW

在你的 xml 中:

<androidx.camera.view.PreviewView
    android:id="@+id/view_finder"
    android:layout_
    android:layout_ />

对于使用 cameraX 的相机实现,您可以参考 https://github.com/android/camera-samples/tree/master/CameraXBasic

【讨论】:

【参考方案4】:

我知道你的问题是什么。您可能正在尝试这样的事情:

textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() 
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) 
        cam.startPreview(surfaceTexture, i, j);
        cam.takePicture();
    

    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1)  
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture)  return false; 
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture)  
);

【讨论】:

需要更多信息。从您的回答中不清楚代码到底有什么问题。

以上是关于在 camera2 apis 中预览延伸的主要内容,如果未能解决你的问题,请参考以下文章

Android Camera2 API:捕获视频而不预览

如何在 Camera2 API Android 5.0 中获取单个预览帧?

camera2(API 级别 21)是不是暴露了所有旧摄像头?

Camera2 API预览方面已损坏

横向模式下的预览方向使用 Camera2 api 顺时针旋转

支持 Android Camera Api 和 Camera2 Api 的问题