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

Android应用中如何从无人机相机流获取Bitmap/RGBA数据?

从DJI无人机相机流提取Bitmap/RGBA数据用于目标检测

我来帮你搞定这个需求——刚好我之前在Android项目里做过DJI视频流和目标检测的集成,给你一步步拆解实现方法:

1. 初始化视频流监听与DJICodecManager

首先你需要注册VideoFeeder的视频数据监听,同时初始化DJICodecManager来处理编码后的视频帧。记得要在DJI SDK初始化完成后做这些操作:

private DJICodecManager mCodecManager;

// 在SDK初始化成功后调用
private void setupVideoStream() {
    // 获取主相机视频流
    VideoFeed primaryFeed = VideoFeeder.getInstance().getPrimaryVideoFeed();
    if (primaryFeed != null) {
        primaryFeed.addVideoDataListener(mVideoDataListener);
    }

    // 初始化CodecManager,传入Context和Surface(如果不需要预览可以传null)
    mCodecManager = new DJICodecManager(this, null, new DJICodecManager.OnFrameAvailableListener() {
        @Override
        public void onFrameAvailable(byte[] yuvData, int width, int height) {
            // 这里拿到解码后的YUV420SP格式数据,接下来转换为Bitmap/RGBA
            processYUVFrame(yuvData, width, height);
        }
    });
}

private final VideoDataListener mVideoDataListener = new VideoDataListener() {
    @Override
    public void onReceive(byte[] videoBuffer, int size) {
        // 将原始编码数据传给CodecManager解码
        if (mCodecManager != null) {
            mCodecManager.sendDataToDecoder(videoBuffer, size);
        }
    }
};

2. 将YUV数据转换为Bitmap/RGBA数组

DJICodecManager输出的是YUV420SP格式的数据(也就是NV21),我们需要把它转换成目标检测需要的Bitmap或者RGBA字节数组。这里推荐两种方式:

方式一:转换为Bitmap

如果你的目标检测算法可以直接接收Bitmap,用这个方法:

private Bitmap processYUVFrame(byte[] yuvData, int width, int height) {
    // 创建YUVImage对象
    YuvImage yuvImage = new YuvImage(yuvData, ImageFormat.NV21, width, height, null);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    // 压缩为JPEG(如果不需要压缩可以调整参数)
    yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, outputStream);
    byte[] jpegData = outputStream.toByteArray();
    
    // 转换为Bitmap
    Bitmap bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
    
    // 注意:如果需要竖屏显示,可能需要旋转Bitmap(根据无人机相机方向调整)
    bitmap = rotateBitmap(bitmap, 90); // 示例旋转90度,根据实际情况调整
    
    return bitmap;
}

private Bitmap rotateBitmap(Bitmap bitmap, int degrees) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degrees);
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

方式二:直接转换为RGBA字节数组

如果目标检测算法需要原始RGBA数据(比如TensorFlow Lite常用的格式),用RenderScript来做转换会更高效:

private RenderScript mRenderScript;
private ScriptIntrinsicYuvToRGB mYuvToRgbScript;
private Allocation mYuvAllocation;
private Allocation mRgbAllocation;
private byte[] mRgbaData;

// 在Activity onCreate中初始化RenderScript
private void initRenderScript() {
    mRenderScript = RenderScript.create(this);
    mYuvToRgbScript = ScriptIntrinsicYuvToRGB.create(mRenderScript, Element.U8_4(mRenderScript));
}

private byte[] convertYUVToRGBA(byte[] yuvData, int width, int height) {
    // 初始化Allocation(只需要第一次做)
    if (mYuvAllocation == null) {
        Type.Builder yuvType = new Type.Builder(mRenderScript, Element.U8(mRenderScript))
                .setX(width)
                .setY(height)
                .setYuvFormat(ImageFormat.NV21);
        mYuvAllocation = Allocation.createTyped(mRenderScript, yuvType.create(), Allocation.USAGE_SCRIPT);

        Type.Builder rgbType = new Type.Builder(mRenderScript, Element.U8_4(mRenderScript))
                .setX(width)
                .setY(height);
        mRgbAllocation = Allocation.createTyped(mRenderScript, rgbType.create(), Allocation.USAGE_SCRIPT);
        
        mRgbaData = new byte[width * height * 4];
    }

    // 传入YUV数据
    mYuvAllocation.copyFrom(yuvData);
    // 执行转换
    mYuvToRgbScript.setInput(mYuvAllocation);
    mYuvToRgbScript.forEach(mRgbAllocation);
    // 取出RGBA数据
    mRgbAllocation.copyTo(mRgbaData);

    return mRgbaData;
}

3. 性能优化要点

  • 缩小分辨率:如果目标检测不需要4K/2K的高分辨率,可以在解码前通过DJICodecManager的setVideoSize方法设置更小的分辨率,减少计算量。
  • 线程处理:视频帧回调和转换操作都在非UI线程,目标检测的推理也应该放到单独的后台线程(比如用AsyncTask或者Coroutine),避免阻塞UI。
  • 资源释放:在Activity销毁或者退出页面时,记得移除视频流监听并释放资源:
@Override
protected void onDestroy() {
    super.onDestroy();
    VideoFeed primaryFeed = VideoFeeder.getInstance().getPrimaryVideoFeed();
    if (primaryFeed != null) {
        primaryFeed.removeVideoDataListener(mVideoDataListener);
    }
    if (mCodecManager != null) {
        mCodecManager.cleanup();
    }
    if (mRenderScript != null) {
        mRenderScript.destroy();
    }
}

注意事项

  • 无人机相机的方向可能会影响画面旋转,你需要根据实际的飞行状态调整Bitmap的旋转角度。
  • 如果是使用TensorFlow Lite等框架,建议直接传入RGBA数组并调整为模型要求的输入尺寸(比如224x224),这样比转换为Bitmap更高效。

内容的提问来源于stack exchange,提问作者Nils Lorentzon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:42:32