You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Camera2 API:如何从onCaptureProgressed的CaptureResult取byte[]数据?

解决Camera2 API实时获取图像byte[]的问题

嘿,我来帮你理清这个问题——其实onCaptureProgressed()里的CaptureResult根本不是用来拿图像像素数据的,它只负责返回捕获过程中的元数据(比如对焦状态、曝光参数这些)。要实时获取图像的byte[],你得用ImageReader来做,这才是Camera2 API里获取帧数据的正确姿势。

核心思路

Camera2 API是通过Surface来输出图像数据的,你需要把ImageReader的Surface添加到捕获会话的输出列表中,当每帧图像捕获完成时,ImageReader会回调通知你获取图像,再把图像转换成byte[]

具体实现步骤

  1. 创建ImageReader实例
    选择合适的图像格式(比如ImageFormat.YUV_420_888,这是Camera2预览常用格式,适合实时处理),指定和预览一致的尺寸,设置最大缓存图像数(一般设2-3帧就够):

    // 假设预览尺寸是previewSize,比如new Size(1920, 1080)
    ImageReader imageReader = ImageReader.newInstance(
        previewSize.getWidth(),
        previewSize.getHeight(),
        ImageFormat.YUV_420_888,
        2 // 最大缓存图像数
    );
    
  2. 设置ImageReader的图像可用监听器
    这里是你获取图像并转换成byte[]的关键地方:

    imageReader.setOnImageAvailableListener(reader -> {
        try (Image image = reader.acquireNextImage()) {
            if (image == null) return;
            // 将Image对象转换成byte[],这里以YUV_420_888为例
            byte[] imageBytes = convertImageToByteArray(image);
            // 在这里处理你的实时图像处理逻辑,比如传入算法模型等
            processImageBytes(imageBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }, new Handler(Looper.getMainLooper()));
    
  3. 将ImageReader的Surface加入捕获会话
    在创建CameraCaptureSession时,把imageReader.getSurface()和预览的Surface(比如TextureView的Surface)一起加入输出列表:

    List<Surface> outputSurfaces = new ArrayList<>();
    outputSurfaces.add(previewSurface); // 预览的Surface
    outputSurfaces.add(imageReader.getSurface()); // ImageReader的Surface
    
    cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            // 配置完成后,启动预览捕获请求
            try {
                CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                previewRequestBuilder.addTarget(previewSurface);
                previewRequestBuilder.addTarget(imageReader.getSurface()); // 把ImageReader的Surface加入请求目标
                session.setRepeatingRequest(previewRequestBuilder.build(), null, backgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            // 处理配置失败逻辑,比如弹出提示
        }
    }, backgroundHandler);
    
  4. 实现Image到byte[]的转换方法
    针对YUV_420_888格式,需要分别提取Y、U、V三个平面的字节数据,再合并(或者根据你的需求单独处理):

    private byte[] convertImageToByteArray(Image image) {
        Image.Plane[] planes = image.getPlanes();
        ByteBuffer yBuffer = planes[0].getBuffer();
        ByteBuffer uBuffer = planes[1].getBuffer();
        ByteBuffer vBuffer = planes[2].getBuffer();
    
        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();
    
        byte[] imageBytes = new byte[ySize + uSize + vSize];
        yBuffer.get(imageBytes, 0, ySize);
        uBuffer.get(imageBytes, ySize, uSize);
        vBuffer.get(imageBytes, ySize + uSize, vSize);
    
        return imageBytes;
    }
    

关键注意事项

  • 一定要在try-with-resources里使用Image对象,或者用完后手动调用image.close(),否则会导致内存泄漏,相机很快就会停止输出图像。
  • 如果你的实时处理需要RGB格式,可以把YUV数据转换成RGB再处理,但这个转换会有一定性能开销,建议尽量在YUV格式下完成处理。
  • onCaptureProgressed()方法确实会每帧调用,但它的作用是告诉你捕获的进度状态,比如自动对焦的进度,不要指望从这里拿图像像素数据。

内容的提问来源于stack exchange,提问作者Mayur Dabhi

火山引擎 最新活动