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

集成Arm Compute库:arm_compute::Image与cv::Mat互转及零拷贝咨询

我之前在项目中把OpenCV和Arm Compute Library(ACL)结合使用时,刚好解决过这两种图像格式转换的问题,分享下我的实践经验:

拷贝数据的转换方式

这种方式兼容性最好,不管内存布局是否一致都能工作,缺点是会产生数据拷贝的开销。

cv::Mat → arm_compute::Image

首先要注意通道顺序:OpenCV默认加载的图像是BGR格式,而ACL的常见彩色格式是RGB(比如FORMAT_RGB888),所以第一步需要转换通道顺序。然后根据cv::Mat的尺寸和数据类型初始化ACL的Image,最后完成数据拷贝:

#include <arm_compute/runtime/Image.h>
#include <opencv2/opencv.hpp>

arm_compute::Image convert_mat_to_acl_image(const cv::Mat& mat) {
    cv::Mat rgb_mat;
    // 转换OpenCV默认的BGR为ACL常用的RGB
    cv::cvtColor(mat, rgb_mat, cv::COLOR_BGR2RGB);

    // 初始化ACL Image的TensorInfo
    arm_compute::TensorShape shape(rgb_mat.cols, rgb_mat.rows);
    arm_compute::TensorInfo info(shape, 1, arm_compute::Format::RGB888);
    arm_compute::Image acl_img;
    acl_img.allocator()->init(info);
    acl_img.allocator()->allocate();

    // 拷贝数据:如果Mat是连续的,直接memcpy;否则逐行拷贝
    if (rgb_mat.isContinuous()) {
        std::memcpy(acl_img.map(), rgb_mat.data, rgb_mat.total() * rgb_mat.elemSize());
    } else {
        const size_t row_stride_acl = acl_img.info()->strides_in_bytes()[1];
        for (int y = 0; y < rgb_mat.rows; ++y) {
            std::memcpy(acl_img.map() + y * row_stride_acl, rgb_mat.ptr(y), rgb_mat.cols * rgb_mat.elemSize());
        }
    }
    acl_img.unmap();

    return acl_img;
}

arm_compute::Image → cv::Mat

反向转换时,同样需要处理通道顺序,把ACL的RGB转回OpenCV的BGR:

cv::Mat convert_acl_image_to_mat(const arm_compute::Image& acl_img) {
    const auto info = acl_img.info();
    // 创建对应尺寸和类型的cv::Mat
    cv::Mat rgb_mat(info->dimension(1), info->dimension(0), CV_8UC3);

    // 拷贝数据
    if (rgb_mat.isContinuous()) {
        std::memcpy(rgb_mat.data, acl_img.map(), rgb_mat.total() * rgb_mat.elemSize());
    } else {
        const size_t row_stride_acl = info->strides_in_bytes()[1];
        for (int y = 0; y < rgb_mat.rows; ++y) {
            std::memcpy(rgb_mat.ptr(y), acl_img.map() + y * row_stride_acl, rgb_mat.cols * rgb_mat.elemSize());
        }
    }
    acl_img.unmap();

    // 转换回OpenCV默认的BGR格式
    cv::Mat bgr_mat;
    cv::cvtColor(rgb_mat, bgr_mat, cv::COLOR_RGB2BGR);
    return bgr_mat;
}
零拷贝共享内存的实现方式

如果想避免数据拷贝,需要确保两者的内存布局完全兼容,这需要满足几个条件:内存对齐一致、步长匹配、通道顺序兼容。

核心前提

  1. 内存对齐:ACL的Image要求内存是16字节对齐的,OpenCV默认创建的cv::Mat(用cv::Mat::create)通常满足这个要求,但如果是手动分配的内存需要额外注意。
  2. 通道顺序:如果ACL支持FORMAT_BGR888(较新版本的ACL已支持),可以直接匹配OpenCV的BGR格式,无需转换通道;否则需要先将cv::Mat转为RGB。
  3. 步长匹配:cv::Mat的行步长(step[0])需要和ACL Image的行步长一致。

cv::Mat → arm_compute::Image(零拷贝)

arm_compute::Image create_acl_image_from_mat_shared(cv::Mat& mat) {
    // 确保Mat是连续的(非连续的话比如ROI无法共享)
    if (!mat.isContinuous()) {
        mat = mat.clone();
    }

    // 如果ACL支持BGR格式,跳过通道转换;否则转为RGB
    arm_compute::Format format = arm_compute::Format::BGR888;
    cv::Mat target_mat = mat;
    if (!arm_compute::data_type_from_format(format).is_valid()) {
        cv::cvtColor(mat, target_mat, cv::COLOR_BGR2RGB);
        format = arm_compute::Format::RGB888;
    }

    // 创建TensorInfo,指定自定义步长(匹配cv::Mat的行步长)
    arm_compute::TensorShape shape(target_mat.cols, target_mat.rows);
    arm_compute::TensorInfo info(shape, 1, format);
    info.set_strides_in_bytes({target_mat.elemSize(), target_mat.step[0]});

    arm_compute::Image acl_img(info);
    // 导入cv::Mat的底层内存
    bool import_success = acl_img.allocator()->import_memory(target_mat.data);
    if (!import_success) {
        // 导入失败(比如内存对齐不满足), fallback到拷贝方式
        return convert_mat_to_acl_image(mat);
    }

    return acl_img;
}

arm_compute::Image → cv::Mat(零拷贝)

cv::Mat create_mat_from_acl_image_shared(arm_compute::Image& acl_img) {
    const auto info = acl_img.info();
    const arm_compute::Format format = info->format();
    int cv_type = CV_8UC3;

    // 获取ACL Image的底层指针(注意:map后不能unmap,直到cv::Mat不再使用)
    uint8_t* acl_data = acl_img.map();
    size_t row_stride = info->strides_in_bytes()[1];

    cv::Mat mat(info->dimension(1), info->dimension(0), cv_type, acl_data, row_stride);

    // 处理通道顺序:如果ACL是RGB格式,转为BGR
    if (format == arm_compute::Format::RGB888) {
        cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR);
    }

    return mat;
}
关键注意事项
  • 生命周期管理:零拷贝时,原始数据的生命周期必须长于新创建的对象。比如用cv::Mat创建的ACL Image,必须保证cv::Mat在ACL Image使用期间不被释放,反之亦然。
  • 非连续内存:如果cv::Mat是非连续的(比如截取ROI得到的Mat),零拷贝无法实现,只能使用拷贝方式。
  • 数据类型匹配:除了8位彩色,还要注意其他数据类型的匹配,比如CV_32FC1对应ACL的FORMAT_F32,CV_16UC1对应FORMAT_U16等,需要根据具体场景调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 06:56:03