当前位置: 首页 > 知识库问答 >
问题:

Android相机预览拉伸

牟星火
2023-03-14

我一直在努力在Android上制作我的自定义相机活动,但当旋转相机时,表面视图的纵横比会变得混乱。

在我的oncreate for the activity中,我设置了framelayout,它保存了显示相机参数的曲面视图。

//FrameLayout that will hold the camera preview
        FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);

        //Setting camera's preview size to the best preview size
        Size optimalSize = null;
        camera = getCameraInstance();
        double aspectRatio = 0;
        if(camera != null){
            //Setting the camera's aspect ratio
            Camera.Parameters parameters = camera.getParameters();
            List<Size> sizes = parameters.getSupportedPreviewSizes();
            optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
            aspectRatio = (float)optimalSize.width/optimalSize.height;
        }

        if(optimalSize!= null){
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            previewHolder.setLayoutParams(params);
            LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            cameraPreview.setLayoutParams(surfaceParams);

        }

        cameraPreview.setCamera(camera);

        //Adding the preview to the holder
        previewHolder.addView(cameraPreview);

然后,在曲面视图中,我设置要显示的相机参数

public void setCamera(Camera camera) {
        if (mCamera == camera) { return; }

        mCamera = camera;

        if (mCamera != null) {
            requestLayout();

            try {
                mCamera.setPreviewDisplay(mHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }


            if(mCamera != null){
                //Setting the camera's aspect ratio
                Camera.Parameters parameters = mCamera.getParameters();
                List<Size> sizes = parameters.getSupportedPreviewSizes();
                Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);

                parameters.setPreviewSize(optimalSize.width, optimalSize.height);
                mCamera.setParameters(parameters);
            }

            /*
              Important: Call startPreview() to start updating the preview surface. Preview must 
              be started before you can take a picture.
              */
            mCamera.startPreview();
        }

    }

您可以看到,当手机旋转时,乐高男会变得更高、更瘦:

如何确保相机视图的纵横比正确?

共有3个答案

厉高逸
2023-03-14

注:我的解决方案是HESAM解决方案的延续:https://stackoverflow.com/a/22758359/1718734

我的地址:Hesam说有些手机上可能会出现一些空白,如下所示:

Hesam提出了第二种解决方案,但这会挤压预览。在某些设备上,它会严重扭曲。

那么我们如何解决这个问题呢。这很简单。。。将纵横比相乘,直到填满屏幕。我注意到,Snapchat、WhatsApp等几种流行应用程序的工作方式都是一样的。

您所要做的就是将其添加到onInstrure方法中:

float camHeight = (int) (width * ratio);
    float newCamHeight;
    float newHeightRatio;

    if (camHeight < height) {
        newHeightRatio = (float) height / (float) mPreviewSize.height;
        newCamHeight = (newHeightRatio * camHeight);
        Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);
        setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);
    } else {
        newCamHeight = camHeight;
        setMeasuredDimension(width, (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);
    }

这将计算屏幕高度,并获得屏幕高度与mPreviewSize高度的比率。然后将相机的宽度和高度乘以新的高度比,并相应地设置测量尺寸。

接下来你知道的是,你最终得到了这个: D

这也适用于前置摄像头。我相信这是最好的方法。现在,我的应用程序只剩下单击“捕获”保存预览本身但是,是的,就是这样。

顾喜
2023-03-14

F1Sher的解决方案很好,但有时不起作用。尤其是当你的surfaceView没有覆盖整个屏幕时。在这种情况下,需要重写onMeasure()方法。我已将代码复制到这里供您参考。

因为我是根据宽度来测量surfaceView的,所以我在屏幕的末端有一点白色的间隙,我是通过设计来填充的。如果您保持高度并将其乘以比率来增加宽度,则可以解决此问题。然而,它会稍微挤压surfaceView。

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

    private Context mContext;
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mContext = context;
        mCamera = camera;

        // supported preview sizes
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for(Camera.Size str: mSupportedPreviewSizes)
                Log.e(TAG, str.width + "/" + str.height);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // empty. surfaceChanged will take care of stuff
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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
        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }

        if (mPreviewSize!=null) {
            float ratio;
            if(mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
  //        setMeasuredDimension((int) (width * ratio), height);
        }
    }

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

        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.height / size.width;
            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;
    }
}
闽涵蓄
2023-03-14

我在用这个方法-

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

        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;
    }

正如您所看到的,您必须输入屏幕的宽度和高度。此方法将根据这些值计算屏幕比率,然后从支持的预览大小列表中选择最适合您的可用大小。在相机对象不为空的地方,使用

mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

然后在onMeasure中,您可以获得如下最佳预览大小:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
           mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }

然后(在我的surfaceChanged方法中的代码中,就像我说的,我正在使用CameraActivity代码的API演示结构,您可以在Eclipse中生成它):

Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();

给你一个提示,因为我和你做了几乎相同的应用程序。相机活动的良好做法是隐藏StatusBar。Instagram等应用程序正在这样做。它会降低您的屏幕高度值并改变您的比率值。在某些设备上可能会出现奇怪的预览尺寸(您的SurfaceView会被削减一点)

为了回答您的问题,如何检查预览比率是否正确?然后获取在中设置的参数的高度和宽度:

mCamera.setParameters(parameters);

您的设置比率等于高度/宽度。如果您希望相机在屏幕上看起来不错,那么您设置给相机的参数的高度/宽度比率必须与屏幕的高度(减去状态栏)/宽度比率相同。

 类似资料:
  • 我在玩谷歌的API2摄像头,我的代码有一些问题。我有两个不同的摄像头,一个用于视频,另一个用于图像。为了提高效率,我将代码更改为使用唯一会话,并使应用程序更高效。 在我这样做之后,我的相机预览不能充分工作。当我使用4:3宽高比时,我的预览会在高处拉伸。换句话说,当我使用16:9的比例时,它看起来很好。在这两种情况下,我的照片看起来都很好,我的意思是,预览不能正确工作,但是我拍的照片有正确的宽高比。

  • 我使用相机仅显示预览(不拍照或录制视频)。 该应用程序始终处于竖屏状态(横向模式被禁用)。相机预览始终旋转90度ccw,我无法更改它(无论是还是和。 预览总是这样旋转还是依赖于设备?如果在纵向模式下总是这样,我可以在之后旋转图像。 或者有没有办法正确设置摄像头?我读了很多关于这个的帖子,但没有一个答案对我有用(Galaxy s2,Android v2.3)

  • 请帮助正确配置Android摄像头(在HTC Desire 2.3.3上测试),以便在旋转(人像)模式下从摄像头预览。预览必须占用屏幕的一半以下,它的宽度必须等于设备的宽度,缩放必须根据相机的纵横比动态设置。我的当前代码: 活动布局: AndroidManifest配置为肖像: 你可以看到图像的比例不对。如何使以适当的比例显示旋转和缩放相机预览? 部分代码取自Mixare开源项目,http://w

  • 这个问题已经问了很多次了,但我似乎找不到我到底想要什么。我试图创建一个相机应用程序,我想在日志中显示YUV或RGB值时,我把相机指向一些颜色。对于RGB或相应的YUV颜色格式,该值必须为0...255范围。我可以管理它们之间的转换,因为在堆栈溢出方面有许多这样的例子。但是,我不能将这些值存储到3个单独的变量中并在日志中显示它们。 任何帮助都将不胜感激。

  • null null 当然,任何其他建议都将不胜感激。 注意:也有类似的问题,我花了几个小时来研究,但没有一个正确地解决了我的问题,也没有给出足够的细节。

  • 有没有人使用过这个打包的Xamrin版本?https://components.xamarin.com/view/emgucv-v3 我最近购买了这个许可证,并击中了与这里描述的完全相同的问题(旋转相机预览到肖像Android OpenCV相机)。一直在使用各种方法。 首先转屏方向从风景到肖像,但这只是以相同的旋转形式(90度)显示流。景观似乎是唯一一个我喜欢的作品。 我尝试使用相机对象旋转相机本