Android CameraX ImageCapture与预览效果不一致,如何实现全屏预览截取中间正方形照片
预览与系统相机不一致的原因
- 相机传感器输出的原始画面比例和你手机屏幕的比例不一致,你直接把预览控件设为全屏时,默认的缩放逻辑会对原始画面做额外裁切放大来填充满屏幕,所以看起来比系统相机预览大。系统相机默认会优先保证预览画面和传感器输出的比例一致,不会强行裁切填充满屏幕,或者用的是等比例居中填充的标准逻辑,和你的自定义缩放逻辑不同。
- 你设置的
setTargetAspectRatio(AspectRatio.RATIO_4_3)和你要的正方形输出需求不匹配,Preview和ImageCapture的输出比例和你预览看到的中间区域的坐标映射没有统一,导致裁剪出来的区域和预览不一致。 - 调用
setViewPort时传入的是中间视图的尺寸,没有做传感器原始坐标到预览视图坐标的转换,所以无效。
实现全屏预览+正方形输出的方案
1. 统一预览缩放逻辑
直接使用官方PreviewView的FILL_CENTER缩放模式,代码如下:
previewView.scaleType = PreviewView.ScaleType.FILL_CENTER
这个模式会将传感器输出的画面等比例缩放,直到填充满整个PreviewView,多余的部分会被等比例裁切,和系统相机的预览逻辑一致,不会出现额外放大的问题,也不需要修改布局的比例限制,不会出现上下白边。
2. 上层叠加半透明遮罩实现预览效果
不要修改PreviewView本身的属性,在布局中给PreviewView上层叠加一个自定义的遮罩View,实现中间正方形镂空、其余区域半透明的效果:
- 自定义View的onDraw方法中,先绘制整个半透明背景
- 再用
PorterDuff.Mode.CLEAR的画笔绘制中间的正方形区域,清掉该区域的透明度,就能露出下方的预览画面,完全不会影响预览本身的属性,也不会丢失透明度。
3. 拍摄后统一裁剪逻辑
拿到ImageProxy后按以下逻辑裁剪得到正方形照片,保证和预览中间区域完全一致:
- 先将ImageProxy转为Bitmap,同时应用ImageInfo中携带的旋转角度,把照片转正
- 取转正后Bitmap宽、高中的较小值作为正方形的边长
- 计算裁剪起始坐标:x = (Bitmap宽度 - 边长) / 2,y = (Bitmap高度 - 边长) / 2
- 用上述参数裁剪Bitmap,得到的就是预览中间正方形区域的内容
参考裁剪代码:
fun cropSquare(imageProxy: ImageProxy): Bitmap { // 先把ImageProxy转成Bitmap,该工具方法可以直接用CameraX官方提供的实现 val originBitmap = ImageProxyUtil.toBitmap(imageProxy) // 应用旋转角度 val matrix = Matrix() matrix.postRotate(imageProxy.imageInfo.rotationDegrees.toFloat()) val rotatedBitmap = Bitmap.createBitmap(originBitmap, 0, 0, originBitmap.width, originBitmap.height, matrix, true) originBitmap.recycle() // 裁剪正方形 val side = min(rotatedBitmap.width, rotatedBitmap.height) val cropX = (rotatedBitmap.width - side) / 2 val cropY = (rotatedBitmap.height - side) / 2 val squareBitmap = Bitmap.createBitmap(rotatedBitmap, cropX, cropY, side, side) rotatedBitmap.recycle() imageProxy.close() return squareBitmap }
内容的提问来源于stack exchange,提问作者Turvy
相关产品推荐
相关产品推荐

