如何裁剪ImageProxy以仅分析预览区域内的数据
裁剪ImageProxy到扫描区域的解决方案
第一步:修正ScannerOverlay的初始化错误
你的ScannerOverlay初始化时重复执行了布局加载,导致binding.scannerOverlay无法正确关联当前视图中的ImageView,先修正这个问题:
class ScannerOverlay @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { private val binding: ScannerOverlayBinding init { // 直接使用binding绑定到当前ConstraintLayout,避免重复inflate binding = ScannerOverlayBinding.inflate(LayoutInflater.from(context), this, true) } val size: Size get() = Size(width, height) // 获取扫描区域在父布局中的比例(用于和ImageProxy尺寸映射) fun getScanRectRatio(): RectF { val overlayView = binding.scannerOverlay val parentWidth = width.toFloat() val parentHeight = height.toFloat() return RectF( overlayView.left / parentWidth, overlayView.top / parentHeight, overlayView.right / parentWidth, overlayView.bottom / parentHeight ) } }
第二步:处理ImageProxy的坐标系转换与裁剪
相机预览的ImageProxy通常和屏幕坐标系存在旋转、缩放差异,需要将扫描区域的比例映射到ImageProxy的实际像素坐标,再完成裁剪:
// 假设在ImageAnalysis.Analyzer的analyze方法中处理 override fun analyze(imageProxy: ImageProxy) { val scannerOverlay = // 获取你的ScannerOverlay实例 val scanRatioRect = scannerOverlay.getScanRectRatio() // 处理ImageProxy的旋转(相机传感器与屏幕方向的差异) val rotationDegrees = imageProxy.imageInfo.rotationDegrees val imageWidth = imageProxy.width val imageHeight = imageProxy.height // 根据旋转角度计算ImageProxy的有效显示尺寸 val (scaledWidth, scaledHeight) = if (rotationDegrees == 90 || rotationDegrees == 270) { imageHeight to imageWidth } else { imageWidth to imageHeight } // 将扫描区域比例转换为适配旋转后的像素坐标 val scaledLeft = scanRatioRect.left * scaledWidth val scaledTop = scanRatioRect.top * scaledHeight val scaledRight = scanRatioRect.right * scaledWidth val scaledBottom = scanRatioRect.bottom * scaledHeight // 根据旋转角度修正坐标,确保裁剪区域和屏幕扫描框对齐 val imageRect = when (rotationDegrees) { 90 -> RectF( scaledTop, scaledWidth - scaledRight, scaledBottom, scaledWidth - scaledLeft ) 180 -> RectF( scaledWidth - scaledRight, scaledHeight - scaledBottom, scaledWidth - scaledLeft, scaledHeight - scaledTop ) 270 -> RectF( scaledHeight - scaledBottom, scaledLeft, scaledHeight - scaledTop, scaledRight ) else -> RectF(scaledLeft, scaledTop, scaledRight, scaledBottom) } // 转换为Int类型Rect用于裁剪 val cropRect = Rect( imageRect.left.toInt(), imageRect.top.toInt(), imageRect.right.toInt(), imageRect.bottom.toInt() ) // 裁剪获取Bitmap(如果不需要Bitmap,可直接操作ImageProxy的planes数据提升效率) val bitmap = imageProxy.toBitmap() val croppedBitmap = Bitmap.createBitmap( bitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height() ) // 此处执行你的分析逻辑(如二维码识别等) // 必须关闭ImageProxy释放资源 imageProxy.close() }
补充说明
getScanRectRatio()通过比例映射适配不同屏幕尺寸,无需关注具体像素值。- 旋转处理是核心:相机预览的方向由
rotationDegrees决定,必须根据角度调整坐标,才能让裁剪区域和屏幕上的扫描框完全对应。 - 若追求性能,可直接操作
ImageProxy.planes中的原始像素数据,跳过Bitmap转换步骤。
内容的提问来源于stack exchange,提问作者Nika Mgaloblishvili
相关产品推荐
相关产品推荐

