android相机预览错误的纵横比

Posted

技术标签:

【中文标题】android相机预览错误的纵横比【英文标题】:android camera preview wrong aspect ratio 【发布时间】:2013-07-22 04:13:04 【问题描述】:

我根据教程创建了一个相机应用程序。我使用的预览类来自 api-Demos“CameraPreview”。我从here 添加了一个修改(预览总是旋转90°)。所以这就是我设置预览大小的方式:

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

    if (display.getRotation() == Surface.ROTATION_0) 
        parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
        mCamera.setDisplayOrientation(90);
    

    if (display.getRotation() == Surface.ROTATION_90) 
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    

    if (display.getRotation() == Surface.ROTATION_180) 
        parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
    

    if (display.getRotation() == Surface.ROTATION_270) 
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        mCamera.setDisplayOrientation(180);
    
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

    requestLayout();

    mCamera.setParameters(parameters);
    mCamera.startPreview();

但预览显示的纵横比错误。是因为上面的代码还是因为我使用的布局?:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_ >

<Button
    android:id="@+id/button_capture"
    android:layout_
    android:layout_
    android:layout_alignParentBottom="true" 
    android:text="@string/capture" />

<FrameLayout
    android:id="@+id/camera_preview"
    android:layout_
    android:layout_/>

那么如何获得正确的纵横比呢?提前致谢。

附:我从以下位置阅读答案:Android camera preview look strange 但这对我不起作用。

【问题讨论】:

也许你可以关闭这个问题? 首先你不必相信,第二我没有时间尝试其他解决方案(发布问题一年后发布)。一旦我尝试了其他答案,我将提供反馈。在那之前一切都很好...... 【参考方案1】:

尝试通过添加此功能来更改预览大小:

private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) 
    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;

    // Find size
    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;

并根据这些优化值设置尺寸:

List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);

parameters.setPreviewSize(optimalSize.width, optimalSize.height);

【讨论】:

正如我在问题中所说,我正在使用此代码(它来自 Api-Demos“CameraPreview”)。我认为问题取决于我的布局以及我如何添加预览? @dermoritz 这不是布局问题!您需要在CameraPreivew.java-class 中将mCamera-object 的mCamera.startPreview() 上方一行的参数之一设置为。为我工作。这个答案应该被接受!【参考方案2】:

以下代码修改相机预览容器的宽度/高度以匹配相机预览的纵横比。

    Camera.Size size = camera.getParameters().getPreviewSize();

    //landscape
    float ratio = (float)size.width/size.height;

    //portrait
    //float ratio = (float)size.height/size.width;

    preview = (FrameLayout) findViewById(R.id.camera_preview);

    int new_width=0, new_height=0;
    if(preview.getWidth()/preview.getHeight()<ratio)
        new_width = Math.round(preview.getHeight()*ratio);
        new_height = cameraPreview.getHeight();
    else
        new_width = preview.getWidth();
        new_height = Math.round(preview.getWidth()/ratio);
    
    preview.setLayoutParams(new FrameLayout.LayoutParams(new_width, new_height));

【讨论】:

好主意,但是应该清除几个错误...... 1. 你应该在你的“if”中用浮点数除以浮点数——现在你正在除整数。 2. cameraPreview 应该是预览。 3. 你应该确保视图的布局是为了执行这个。【参考方案3】:

使用上面的解决方案,使用方法private Size getOptimalPreviewSize(List sizes, int w, int h)。 工作得很好!我在纵向的纵横比方面遇到问题:这是我使用的解决方案。将其与 android 的文档混合:

  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.

            if (mHolder.getSurface() == null)
              // preview surface does not exist
              return;
            


            // stop preview before making changes
            try 
                mCamera.stopPreview();
             catch (Exception e)
              // ignore: tried to stop a non-existent preview
            

            // set preview size and make any resize, rotate or
            // reformatting changes here
            Camera.Parameters params = mCamera.getParameters();
            params.set("orientation", "portrait");
            Size optimalSize=getOptimalPreviewSize(params.getSupportedPreviewSizes(),  getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
            params.setPreviewSize(optimalSize.width, optimalSize.height);
            mCamera.setParameters(params);
            // start preview with new settings
            try 

                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();

             catch (Exception e)
                Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            
        
    

【讨论】:

【参考方案4】:

问题实际上在于您布局事物的方式。 在Preview 类中有覆盖onLayout。它的工作思路是根据找到的最佳Size 设置子SurfaceView 大小。但是没有考虑轮换,需要自己动手:

  if (mPreviewSize != null) 
    previewWidth = mPreviewSize.height;
    previewHeight = mPreviewSize.width;
  

而不是

  if (mPreviewSize != null) 
    previewWidth = mPreviewSize.width;
    previewHeight = mPreviewSize.height;
  

技巧是交换宽度和高度,这是因为

实现了 90 度旋转
  mCamera.setDisplayOrientation(90);

您也可以考虑根据您设置的方向分叉子预览大小设置

  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
      //...
  

(在我提供的代码中,它始终是 90 度旋转,对于 180 度,您不必做任何事情,当您不设置任何旋转时,无需交换宽度和高度)

另一件值得一提的事情 - 在计算 getOptimalPreviewSize 时,当你有旋转并且你交换子宽度和高度时,你还应该传递父(Preview)宽度和高度在 onMeasure 中交换:

if (mSupportedPreviewSizes != null) 
  //noinspection SuspiciousNameCombination
  final int previewWidth = height;
  //noinspection SuspiciousNameCombination
  final int previewHeight = width;
  mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, previewWidth, previewHeight);

【讨论】:

【参考方案5】:

Henric 的回答对我不起作用,因此我创建了另一种方法,该方法在给定目标视图当前宽度和高度以及活动方向的情况下确定任何相机的最佳预览尺寸:

public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) 
    if (CommonUtils.isEmpty(cameraPreviewSizes)) 
        return null;
    

    int optimalHeight = Integer.MIN_VALUE;
    int optimalWidth = Integer.MIN_VALUE;

    for (Camera.Size cameraPreviewSize : cameraPreviewSizes) 
        boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width;
        int actualCameraWidth = cameraPreviewSize.width;
        int actualCameraHeight = cameraPreviewSize.height;

        if (isActivityPortrait) 
            if (!isCameraPreviewHeightBigger) 
                int temp = cameraPreviewSize.width;
                actualCameraWidth = cameraPreviewSize.height;
                actualCameraHeight = temp;
            
         else 
            if (isCameraPreviewHeightBigger) 
                int temp = cameraPreviewSize.width;
                actualCameraWidth = cameraPreviewSize.height;
                actualCameraHeight = temp;
            
        

        if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) 
            // finds only smaller preview sizes than target size
            continue;
        

        if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) 
            // finds only better sizes
            optimalWidth = actualCameraWidth;
            optimalHeight = actualCameraHeight;
        
    

    Size optimalSize = null;

    if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) 
        optimalSize = new Size(optimalWidth, optimalHeight);
    

    return optimalSize;

这使用了自定义 Size 对象,因为 Android 的 Size 在 API 21 之后可用。

public class Size 

    private int width;
    private int height;

    public Size(int width, int height) 
        this.width = width;
        this.height = height;
    

    public int getHeight() 
        return height;
    

    public int getWidth() 
        return width;
    


您可以通过侦听视图的全局布局更改来确定视图的宽度和高度,然后您可以设置新的尺寸。这也显示了如何以编程方式确定活动方向:

cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() 
            @Override
            public void onGlobalLayout() 
                // gets called after layout has been done but before display.
                cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);

                boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
                Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait);
                if (optimalCameraPreviewSize != null) 
                    LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight());
                    cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams);
                
            
        );

【讨论】:

以上是关于android相机预览错误的纵横比的主要内容,如果未能解决你的问题,请参考以下文章

4:3 平板电脑上的 Android 相机预览纵横比

确保相机预览大小/纵横比与生成的视频相匹配

Android:如何计算图像的纵横比?

Xamarin android Camera2 - 在 18:9 纵横比设备上拉伸图像预览

Android相机预览人像比例

Android Camera 2 预览尺寸和设备纵横比