你真的懂了Camera的尺寸参数了吗?
你真的懂了Camera的尺寸参数了吗?
本文针对自定义相机开发中,使用opengl渲染时,引发的最终视图变形问题
开发相机的过程中,你是否遇到过你想要的是右边图效果,结果出来的却是左边图效果,如果你遇到了,本文你遇到的问题或许有帮助;引发上面的不同效果,就是因为一些列的尺寸所引发的问题。
自定义相机大致框架
一般来说,自定义相机设计如上图:
- 底层camera配置好预览参数、方向等尺寸以及配置预览回调到SurfaceTexture,同时创建新纹理并绑定到SurfaceTexture上
- 第一步预览开启时,数据会流到SurfaceTexture的纹理上,将其绘制在上图中间的FBO上,在FBO里面我们可以完成自定义相机的内容,比如滤镜、分屏、美颜等各种特效
- 取出上一步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的附着纹理进行绘制即可
上述会引发几个问题?
- 如何根据期望尺寸expect,去Camera的众多预览尺寸中去选择合适的尺寸?
- 如何设置相机角度?
- 预览尺寸数据长度是如何去计算?
- 最后尺寸之间联系如何?
如何根据期望尺寸expect,去Camera的众多预览尺寸中去选择合适的尺寸?
以下只是提供思路:
- 期望尺寸宽高比逐一与预览尺寸宽高比对比,查看是否相等,相等就可以选择,如果相等的有很多,可以在从中选择最小、最大或者居中的尺寸;但是越大分辨率越高,最后预览回调的数据data越多,处理也越耗时
List<Camera.Size> useableSize = new ArrayList<>();
for (Camera.Size size : sizes) {
if (size.height * expectWidth / expectHeight == size.width) {
useableSize.add(size);
}
}
- 如果宽高比都没有一致的,那可以按照以下几个规则去选择:
- 期望尺寸乘积和预览尺寸乘积接近的
- 期望宽和预览宽相等或者接近的
- 期望高和预览高相等或者接近的
如何设置相机角度
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;
尺寸关系?
- 根据上面章节如何根据期望尺寸,选择预览尺寸,得到预览尺寸
- 根据相机角度,竖屏要调整宽高,这个宽高值选择的预览宽高
- 将FBO宽高设置为预览宽高,因为要装下所有的数据,第二步已经通过相机角度调整了宽高,这里就不用调整宽高了
- 最后渲染到view,使用视图View宽高即可
更多精彩博文,加入我们,一同进步!