OpenCV 和 TensorRT 之间的数据转换 HWC to CHW

发布时间 2023-05-30 17:58:49作者: cvhuang

TensorRT做图像相关模型部署的时候,导入图片的数据存储往往是BHWC(Batch, Height, Width, Channel), 而TensorRT推理的时候是BCHW. OpenCV 和 TensorRT 之间的数据转换(BHWC to BCHW),一般是所有元素遍历赋值:

    cv::Mat origin_image = cv::imread("test.jpg", 1);
    if (!origin_image.data)
    {
        cerr << "Error : could not load image." << endl;
        return false;
    }
    cv::Mat resize_image;
    cv::resize(origin_image, resize_image, cv::Size(inputH, inputW), cv::INTER_CUBIC);

    // Fill data buffer
    float* hostDataBuffer = static_cast<float*>(buffers.getHostBuffer(mParams.inputTensorNames[0]));
    for (int i = 0, volImg = inputC * inputH * inputW; i < batchSize; ++i)
    {
        for (int c = 0; c < inputC; ++c)
        {
            for (unsigned j = 0, volChl = inputH * inputW; j < volChl; ++j)
                hostDataBuffer[i * volImg + c * volChl + j] = 1.0 - float(resize_image.data[j * inputC + 2 - c]/255.);
        }
    }

这种方式简单易懂,不过还可以用OpenCV的方式来做数据转换,效率也会提高很多。例如再GPU上操作,用OpenCV里面的split把图片分3通道,然后直接整块赋值:

    // example 2
float* deviceDataBuffer = static_cast<float*>(buffers.getDeviceBuffer(mParams.inputTensorNames[0])); std::vector<cv::cuda::GpuMat> chw; for(size_t i = 0 , volImg = inputC*inputH*inputW, volChl = inputH*inputW; i < batchSize; i++ ) { std::vector<cv::cuda::GpuMat> chw { cv::cuda::GpuMat(inputH, inputW, CV_32F, &deviceDataBuffer[i*volImg + 0*volChl]), cv::cuda::GpuMat(inputH, inputW, CV_32F, &deviceDataBuffer[i*volImg + 1*volChl]), cv::cuda::GpuMat(inputH, inputW, CV_32F, &deviceDataBuffer[i*volImg + 2*volChl]) }; cv::cuda::split(G_images[i], chw); // G_images是多张图片,数据类型是CV::cuda::GpuMat }

和第一个例子对比,元素赋值放到split里面,是在gpu上操作,所以速度会快很多。同时不用buffers.copyInputToDevice().

其它方式:

  • 有人用cuda的代码写了一个[1],写了转换的kernel,具体效率没有测试过,应该差不多,不过编译需要cuda会麻烦一点
  • 在CPU上实现exmaple 2. 效率一样也有提升,因为cpu上的cv::split里面用了多线程,还有块处理的方式,也比直接一个一个遍历要快
  • OpenCV里面的DNN模块也有个函数blobfromImage()可以在cpu转换,不过不够灵活

Ref:

[1] https://www.dotndash.net/2023/03/09/using-tensorrt-with-opencv-cuda.html#use-tensorrt-c-api-with-opencv