欢迎关注我的个人博客:http://www.offday.club/
问题现象
之前负责人脸识别的模块,遇到一个很奇怪的问题。录入时,人脸预览有拉伸的迹象的(之前的项目都是好好的),后面花了一天半的时间,终于将此问题解决,故想写一篇总结,记录自己的解决问题的过程。
PS:公司不同品牌的手机很多,其他手机都没有预览问题,单独有一个项目存在着预览拉伸的问题。
问题背景
使用的自定义surfaceview
当前使用的是camera API2 (因为Android P 版本不支持HAL1.0了,所以camera api1也被我们渐渐舍弃了=-=)
分析过程
第一直觉:
当初自己的第一直觉是,emmm,这应该是跟分辨率有关系。因为其他没有问题的手机,我看了下分辨率,都是7201500;而唯独发生拉伸现象的手机是10802160的。
于是我就在自定义Surfaceview中,看了下Surfaceview的大小,发现:
720*1500 手机width:480 height:640
1080*2160 手机width:720 height:960
Emmm,其实可以理解,不同分辨率,surfaceview的大小会进行对应调整。但不难发现,宽高比是一致的。
所以,我排除了跟分辨率的关系。
以下为自定义view中onMeasure的重写部分:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.e("onMeasure", "widthMeasureSpec = " +widthSize + " heightMeasureSpec = " + heightSize + "height = " + height);
setMeasuredDimension(widthSize, heightSize);
}
了解产生拉伸的原因:
之后,上网查了一些资料,camera previewSize需要跟surfaceview的大小比例不一致就会产生拉伸效果。(我认为的比例一致就是宽高比是一致的,但我解完这个问题后,只想说,不知道camera的预览方向,真心难以理解)。
如果你已经看了上面的两篇文档,那么就不难理解了。因为camera默认的取景方向是横向的,所以上面的话我们应该如下理解:Camera previewSize的宽高比跟surfaceview的高宽最简比一致,就不会拉伸。
举个例子:
可以看下我附件的Demo(利用camera1预览,写的非常简单,仅仅提供了个预览功能,只是为了验证这个例子而已),如果我们不设置setDisplayOrientation来改变预览角度,那么预览就会是横屏的,所以按照横屏的方式来计算,surfaceview的宽度实际对应着高度,从而验证上述的想法。
当然很多情况下,camera previewSize跟surfaceview的最简比乘积不一定一致,那么
这个时候预览就肯定会拉伸,但是为了防止拉伸(压缩)明显,就要找跟previewSize最为相近的比例去设置surfaceview的宽跟高。
拿我下面获取的camera previewSize为例,比如说:
全面屏手机比例是1500720。而surfaceview恰好是全屏的,那么此时camera previewSize其实并没有1500720的比例,你就需要设置最为接近的1600*800的previewSize,这样才可以将拉伸(压缩)的效果降到最低。反之相同。
那知道了这个产生问题,后面的问题就都好说了。
如何获取camera 预览尺寸:
很明显,此问题的两个重要关键点就是camera previewSize以及surfaceview的Size大小,那么,我们首先要做的,就需要优先获取当前手机camera previewSize所支持的那些?
Camera APi2 获取camera预览尺寸的方法:
private void choosePreview() {
try {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics("1");
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] preview = map.getOutputSizes(SurfaceHolder.class);
for (int i = 0 ;i <= preview.length ; i++) {
Log.d(TAG,"preview width = " + preview[i].getWidth() + ", height = " + preview[i].getHeight());
}
} catch (Exception exception) {
}
}
Camera API1 获取camera previewSize的方法:
List<Camera.Size> allSupportedSize = camPara.getSupportedPreviewSizes();
文章最后见手机支持的尺寸。
如何设置camera previewSize:
虽然知道了手机camera previewSize的大小,但是如果不知道如何设置,其实也算是白搭。
Camera API1 设置camera previewSize:
params.setPreviewSize(cameraWidth, cameraHeight);
Camera API2 设置previewSize:
Surfaceview
mSurfaceHolder.setFixedSize(heightSize, widthSize);// 此方法是设置surface的size的,可以认为surface的size代表camera的PreviewSize
2.TextureView (参考官方Demo)
SurfaceTexture texture = mTextureView.getSurfaceTexture();
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
万事具备了,东风都不需要了,后面只需要将上面所提到的方法运用到自己具体的例子中就好了。
拿我的自定义view来说:
为了简单,我将设置surfaceview的大小跟设置camera previewSize的大小写在一起了。也方便后面的同事进行排查。
以下为我的修改方案,仅仅是加了一行代码,就解决了拉伸问题。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
...
Log.e("onMeasure", "widthMeasureSpec = " +widthSize + " heightMeasureSpec = " + heightSize + "height = " + height);
//添加了如下代码
mSurfaceHolder.setFixedSize(heightSize, widthSize);
setMeasuredDimension(widthSize, heightSize);
}
PS:以下是 setFixedSize的方法描述,可以看到传参先是宽,之后是高。但是我自己项目中却传入的是setFixedSize(heightSize, widthSize)。
/**
* Make the surface a fixed size. It will never change from this size.
* When working with a {@link SurfaceView}, this must be called from the
* same thread running the SurfaceView's window.
*
* @param width The surface's width.
* @param height The surface's height.
*/
public void setFixedSize(int width, int height);
其实有一点是需要注意的,那就是camera previewSize的宽一般要大于高的。
详细参考:https://blog.csdn.net/mengzhengjie/article/details/70859396
参考链接:
https://blog.csdn.net/yanzi1225627/article/details/17652643
https://blog.51cto.com/ticktick/1592267
https://blog.csdn.net/mengzhengjie/article/details/70859396
总结:
此种修改仅仅解决的是预览拉伸的问题,拍照拉伸等问题后续会慢慢了解的,但是我相信,万变不离其宗,将本文中的精髓了解到,拍照拉伸的问题依旧迎刃而解了。
小弟初步学习Camera,有什么不对的地方还请各位指正。
Demo下载地址:
大家看下百度云吧。
提取码:w5bk
以下是我debug用的手机支持的部分尺寸:
Line 229: 03-04 22:33:28.565 1887 1887 D Camera: preview width = 1600, height = 800
..
Line 234: 03-04 22:33:28.565 1887 1887 D Camera: preview width = 960, height = 720
Line 235: 03-04 22:33:28.565 1887 1887 D Camera: preview width = 960, height = 540
..
Line 241: 03-04 22:33:28.565 1887 1887 D Camera: preview width = 176, height = 144