You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CameraX ImageAnalysis输出损坏碎片化 部分设备二维码扫描异常

问题根源

你的图像破碎问题是由相机YUV帧的行步长(rowStride)对齐机制导致的:

  • 多数设备(比如Pixel)输出的YUV帧行步长等于图像宽度,无额外填充字节,所以直接读取整个buffer的逻辑可以正常运行
  • 少数设备为了硬件性能对齐,会在每行YUV数据末尾添加填充字节,你直接读取整个buffer的逻辑会把填充字节也算入有效图像数据,传入zxing的PlanarYUVLuminanceSource后就会出现画面错位、破碎的问题

修复方案

1. 修正Y通道数据读取逻辑

将原来直接读取整个buffer的逻辑替换为逐行拷贝,跳过每行的填充字节,代码修改如下:

override fun analyze(image: ImageProxy) {
    val yPlane = image.planes[0]
    val rowStride = yPlane.rowStride
    val pixelStride = yPlane.pixelStride
    val imageWidth = image.width
    val imageHeight = image.height

    // 初始化刚好容纳有效Y数据的数组,大小固定为宽*高
    if (imageData.size != imageWidth * imageHeight) {
        imageData = ByteArray(imageWidth * imageHeight)
    }
    val buffer = yPlane.buffer
    buffer.rewind()

    // 逐行拷贝有效像素,跳过填充字节
    for (row in 0 until imageHeight) {
        val rowOffset = row * imageWidth
        // 定位到当前行的起始位置
        buffer.position(row * rowStride)
        if (pixelStride == 1) {
            // 绝大多数设备Y通道像素步长为1,直接拷贝整行有效数据
            buffer.get(imageData, rowOffset, imageWidth)
        } else {
            // 兼容特殊设备,逐像素读取跳过间隔
            for (col in 0 until imageWidth) {
                imageData[rowOffset + col] = buffer.get()
                buffer.position(buffer.position() + pixelStride - 1)
            }
        }
    }

    // 以下是你原来的裁剪计算逻辑,保留即可
    val iFact = if (mActivity.getOverlayView().width <= mActivity.getOverlayView().height) {
        imageWidth / mActivity.getOverlayView().width.toDouble()
    } else {
        imageHeight / mActivity.getOverlayView().height.toDouble()
    }
    val size = mActivity.getOverlayView().size * iFact
    // 增加边界校验,避免裁剪区域超出图像范围
    val cropLeft = ((imageWidth - size) / 2).toInt().coerceAtLeast(0)
    val cropTop = ((imageHeight - size) / 2).toInt().coerceAtLeast(0)
    val cropSize = size.toInt().coerceAtMost(minOf(imageWidth - cropLeft, imageHeight - cropTop))

    val source = PlanarYUVLuminanceSource(
        imageData,
        imageWidth, imageHeight,
        cropLeft, cropTop,
        cropSize, cropSize,
        false
    )

    // 剩余原有逻辑保持不变
    // ... 缩略图渲染、二维码解码、FPS计算、image.close()等
}

2. 优化ImageAnalysis配置

在构建ImageAnalysis实例时增加格式和旋转配置,确保输出图像符合预期:

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(960, 960))
    // 固定输出YUV420格式,避免不同设备返回其他格式
    .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
    // 开启自动旋转,输出图像方向自动适配屏幕方向,无需额外转换
    .setOutputImageRotationEnabled(true)
    .build()

修改完成后即可兼容所有设备的YUV帧输出,裁剪后的图像不会再出现破碎问题。


内容的提问来源于stack exchange,提问作者Shyam Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 04:57:02