简体   繁体   English

纵向模式下的相机风景预览

[英]Camera landscape preview in portrait mode

I have a dialog design where camera preview should be landscape in portrait mode (see pics). 我有一个对话框设计,其中相机预览应该在人像模式下为横向(请参见图片)。 I choose preview size correctly through getOptimalPreviewSize() and when I don't use setDisplayOrientation(90) I get this: 我通过getOptimalPreviewSize()正确选择了预览大小,当我不使用setDisplayOrientation(90)时,我得到了:

在此处输入图片说明

When I use setDisplayOrientation(90) I get this: 当我使用setDisplayOrientation(90)时,我得到了:

在此处输入图片说明

Does anybody have any ideas how to fix it? 有人有任何解决办法的想法吗? Can android do that kind of thing? android可以做这种事情吗?

Answer (thanks to Eddy Talvala): 答案(感谢Eddy Talvala):
Due to you can't process the whole cam picture in portrait when your cam view in xml is horizontal, you should crop. 由于当您在xml中的cam视图为水平时无法以纵向方式处理整个cam图片,因此应进行裁剪。 I decided to fit horizontally and crop all that in the bottom: 我决定水平放置并在底部裁剪所有内容:

|           |
| _________ |   _____
||         ||  |     |
||         ||  | ^-^ |
||  ______ ||  | \_/ |
|| | ^-^  |||  |  |  |    
|| |_\_/_ |||  |__|__|
||   image ||   sensor
||         ||
||         ||
||_________||
||  < O [] ||
|___________|
    device

You should use TextureView to specify matrix for fit and cropping. 您应该使用TextureView为拟合和裁剪指定矩阵。 Something like that (kotlin): 像这样的东西(kotlin):

fun camInit() {
    val camParam = camera.parameters
    val optimalSize = getOptimalPortraitPreviewSize(camParam.supportedPreviewSizes, width, height)

    camParam.setPreviewSize(optimalSize.width, optimalSize.height)
    camera.parameters = camParam

    camera.setPreviewTexture(surface)

    val scaleDelta = optimalSize.height - preview.width // for portrait
    val scaleY: Float = (optimalSize.width.toFloat() - scaleDelta) / preview.height // for portrait

    val matrix = Matrix()
    matrix.setScale(1f, scaleY)    // 1f cause we fit horizontally

    preview.setTransform(matrix)

    camera.startPreview()
}

Notice that for portrait mode you should use specific getOptimalProtraitPreviewSize(), cause standard func that everybody uses to get camera preview optimal size (with ASPECT_TOLERANCE and other things) can return you a size with a small resolution. 请注意,对于人像模式,您应该使用特定的getOptimalProtraitPreviewSize(),因为每个人都可以使用标准功能来获得相机预览的最佳尺寸(使用ASPECT_TOLERANCE等),可以返回具有较小分辨率的尺寸。 Try something like this: 尝试这样的事情:

fun getOptimalPortraitPreviewSize(sizes: List<Camera.Size>, previewWidth: Int) {

    var selectedSize: Camera.Size = sizes[0]

    for (size in sizes) {
        // use size's height cause of portrait
        val currentSizeWidthDelta = abs(size.height - previewWidth)
        val selectedSizeWidthDelta = abs(selectedSize.height - previewWidth)
        if (currentSizeWidthDelta < selectedSizeWidthDelta) selectedSize = size
    }

    return selectedSize
}

The long edge of the image sensor lines up with the long edge of the device. 图像传感器的长边与设备的长边对齐。

(bad ascii art):
____________
|           |
| _________ |   _____
||         ||  |     |
||         ||  |     |
||         ||  |     |
||         ||  |     |
||         ||  |_____|
||         ||   sensor
||         ||   physical 
||         ||   orientation
||_________||
||  < O [] ||
|___________|
    device

Given that, there's no way you can draw the image like this: 鉴于此,您无法像这样绘制图像:

_____________
|           |
| _________ |   _____
||         ||  |     |
||         ||  |     |
||  ______ ||  |     |
|| |      |||  |     |
|| |______|||  |_____|
||   image ||   sensor
||         ||
||         ||
||_________||
||  < O [] ||
|___________|
    device

unless you either rotate the image (in which case up isn't up): 除非您旋转图像(在这种情况下不向上旋转):

_____________
|           |
| _________ |   _____
||         ||  |     |
||         ||  | ^-^ |
||  ______ ||  | \_/ |
|| | <-\__|||  |  |  |    
|| |_<-/__|||  |__|__|
||   image ||   sensor
||         ||
||         ||
||_________||
||  < O [] ||
|___________|
    device

or you stretch the image horizontally, which looks pretty bad: 或者您水平拉伸图像,看起来很糟糕:

_____________
|           |
| _________ |   _____
||         ||  |     |
||         ||  | ^-^ |
||  ______ ||  | \_/ |
|| | ^___^|||  |  |  |    
|| |___|__|||  |__|__|
||   image ||   sensor
||         ||
||         ||
||_________||
||  < O [] ||
|___________|
    device

or you crop a slice of the image, which reduces the FOV a lot: 或裁剪图像的一部分,从而大大降低了FOV:

_____________
|           |
| _________ |   _____
||         ||  |     |
||         ||  | ^-^ |
||  ______ ||  | \_/ |
|| | ^-^  |||  |  |  |    
|| |_\_/__|||  |__|__|
||   image ||   sensor
||         ||
||         ||
||_________||
||  < O [] ||
|___________|
    device

Because the image sensor is landscape when the phone is landscape, there's no way to place a landscape preview in a portrait UI without one of those three things happening. 因为当手机处于横向时图像传感器是横向的,所以如果没有这三种情况之一,就无法在纵向UI中放置横向预览。 You either need a portrait preview in a portrait UI, or you need to crop the image down. 您可能需要在肖像UI中进行肖像预览,或者需要裁剪图像。 That's not a limitation of Android, it's just a limitation of geometry. 这不是Android的限制,只是几何的限制。

If you want to crop, you'll probably want to send the camera data to a SurfaceTexture, and crop in OpenGL, if you want a live preview. 如果要裁剪,则可能需要将相机数据发送到SurfaceTexture,然后如果要实时预览,则要在OpenGL中裁剪。

If you are using the Camera2 API. 如果您正在使用Camera2 API。 When get mPreviewSize by ChooseOptimalSize you can swap the width and height. 当通过ChooseOptimalSize获得mPreviewSize时,可以交换宽度和高度。 (The width and height is from your view). (宽度和高度从您的角度来看)。 And then 接着

textureView.setAspectRatio(mPreviewSize.Height, mPreviewSize.Width);

When create preview session 创建预览会话时

texture.SetDefaultBufferSize(mPreviewSize.Width, mPreviewSize.Height);

Then you will preview the normal image 然后您将预览普通图像

Thanks @Eddy Talvala and @blinker! 感谢@Eddy Talvala和@blinker! I bring my function to correctly update the sizes in any orientation: 我带上我的功能可以在任何方向正确更新尺寸:

  public void invalidateSize(TextureView textureView) {
        Point cameraResolution = getCameraResolution();
        int previewRotation = getPreviewRotation();

        // Camera and preview on screen in the opposite orientation
        if (previewRotation == 90 || previewRotation == 270) {
            //noinspection SuspiciousNameCombination
            cameraResolution = new Point(cameraResolution.y, cameraResolution.x);
        }

        float cameraAspectRatio = cameraResolution.x / (float) cameraResolution.y;
        float textureAspectRatio = textureView.getWidth() / (float) textureView.getHeight();

        float scaleX = cameraAspectRatio / textureAspectRatio;
        float scaleY = textureAspectRatio / cameraAspectRatio;

        if (scaleX > scaleY) {
            scaleY = 1;
        } else {
            scaleX = 1;
        }

        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY,
                textureView.getWidth() / 2f, textureView.getHeight() / 2f);

        textureView.setTransform(matrix);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM