你真的懂了Camera的尺寸参数了吗?

你真的懂了Camera的尺寸参数了吗?

本文针对自定义相机开发中,使用opengl渲染时,引发的最终视图变形问题

在这里插入图片描述
开发相机的过程中,你是否遇到过你想要的是右边图效果,结果出来的却是左边图效果,如果你遇到了,本文你遇到的问题或许有帮助;引发上面的不同效果,就是因为一些列的尺寸所引发的问题。


自定义相机大致框架

在这里插入图片描述
一般来说,自定义相机设计如上图:

  1. 底层camera配置好预览参数、方向等尺寸以及配置预览回调到SurfaceTexture,同时创建新纹理并绑定到SurfaceTexture上
  2. 第一步预览开启时,数据会流到SurfaceTexture的纹理上,将其绘制在上图中间的FBO上,在FBO里面我们可以完成自定义相机的内容,比如滤镜、分屏、美颜等各种特效
  3. 取出上一步FBO的附着纹理id,将此纹理绘制到上图最右边View提供的渲染环境中去,最终显示到屏幕上

但是,在上面渲染的时候三个地方的尺寸各自代表什么意思?他们之间又有什么关系?
他们的确有关系,如果处理不当,最终view上显示的图像就会变形


size解释

  • Camera

期望尺寸expect: 我们期望相机预览输出的尺寸,比如1920*1680;但是最终相机预览尺寸不一定是我们期望的expect,不一定相等

预览尺寸preview: Android Camera都有自己固定的多种预览尺寸,,前后摄像头预览尺寸也尽不相同,我们需要根据我们期望的尺寸与预览尺寸对比,从中选择出适合我们的预览尺寸

旋转角度: Android相机都是有角度的,我们需要设置角度使我们的图像看起来是正常方向;而且根据横竖屏方向,适当调整宽高(宽变高,高变宽)

预览数据长度: Android相机大部分数据格式都是YUV420,只是存储格式会有平面、压缩模式,YV12、YU12、NV12和NV21,而且数据长度肯定是预览尺寸乘积,再乘3/2

  • FBO
    帧缓冲对象FBO在创建附着纹理,以及渲染时窗口需要高宽尺寸参数,这个参数是多少呢?一般来说没有限制,但是为了能够接住camera那边传递过来的所有数据,至少要比预览尺寸大

  • View视图
    这边来说,一般view尺寸布局时就固定了的,这个尺寸最终要用到渲染最后一级图像时,渲染整个窗口时使用,纹理则使用上一级FBO的附着纹理进行绘制即可

上述会引发几个问题?

  1. 如何根据期望尺寸expect,去Camera的众多预览尺寸中去选择合适的尺寸?
  2. 如何设置相机角度?
  3. 预览尺寸数据长度是如何去计算?
  4. 最后尺寸之间联系如何?

如何根据期望尺寸expect,去Camera的众多预览尺寸中去选择合适的尺寸?

以下只是提供思路:

  1. 期望尺寸宽高比逐一与预览尺寸宽高比对比,查看是否相等,相等就可以选择,如果相等的有很多,可以在从中选择最小、最大或者居中的尺寸;但是越大分辨率越高,最后预览回调的数据data越多,处理也越耗时
List<Camera.Size> useableSize = new ArrayList<>();
for (Camera.Size size : sizes) {
    if (size.height * expectWidth / expectHeight == size.width) {
       useableSize.add(size);
    }
}
  1. 如果宽高比都没有一致的,那可以按照以下几个规则去选择:
  • 期望尺寸乘积和预览尺寸乘积接近的
  • 期望宽和预览宽相等或者接近的
  • 期望高和预览高相等或者接近的

如何设置相机角度

Camera都会有一个默认的角度,如果不设置相机角度,直接输出的图片会不那么正,所以我们要调整这个角度

Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(camerId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
        .getRotation();
int degrees = 0;
switch (rotation) {
    case Surface.ROTATION_0:
        degrees = 0;
        break;
    case Surface.ROTATION_90:
        degrees = 90;
        break;
    case Surface.ROTATION_180:
        degrees = 180;
        break;
    case Surface.ROTATION_270:
        degrees = 270;
        break;
}

int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
    result = (info.orientation + degrees) % 360;
    result = (360 - result) % 360;
} else {
    result = (info.orientation - degrees + 360) % 360;
}
//以下camera是打开Camera后的相机索引
camera.setDisplayOrientation(result);

预览回调数据长度是如何计算的?

首先,你得知道什么是YUV420
我们知道预览尺寸就是显示的分辨率,假如一张YUV格式的图分辨率为100 * 100,那么就有100 * 100个像素点,每个像素点由三个通道Y U V,明亮程度和颜色差值构成,420是比例,U V在宽高上都只有Y的一半,所以Y有100 * 100, U只有100 * 100 /4 ,V和U一样,最终加起来就是100 * 100 * 3 / 2;


尺寸关系?

  1. 根据上面章节如何根据期望尺寸,选择预览尺寸,得到预览尺寸
  2. 根据相机角度,竖屏要调整宽高,这个宽高值选择的预览宽高
  3. 将FBO宽高设置为预览宽高,因为要装下所有的数据,第二步已经通过相机角度调整了宽高,这里就不用调整宽高了
  4. 最后渲染到view,使用视图View宽高即可

更多精彩博文,加入我们,一同进步!

在这里插入图片描述

发布了148 篇原创文章 · 获赞 41 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/103923595