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

Posted cvhuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 和 TensorRT 之间的数据转换 HWC to CHW相关的知识,希望对你有一定的参考价值。

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把图像分多通道,然后直接整块赋值:

    // 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

 

opencv mat和CImage之间的转换

【中文标题】opencv mat和CImage之间的转换【英文标题】:Conversion between opencv mat and CImage 【发布时间】:2014-08-26 16:28:22 【问题描述】:

我正在寻找一种将存储在 opencv Mat 变量中的图像转换为 CImage 对象的有效方法(CBitmap 也可以)。我所说的高效是指快速和“简写”。 我的扩展搜索导致找到手动将位图标题和数据写入对象的代码,我想避免这种情况,因为我不太明白那里发生了什么。更糟糕的是,它看起来有点被弃用并产生运行时错误...... (我相信我在这里得到它:http://howto-code-it.blogspot.com/2011/05/how-to-convert-iplimage-to-hbitmap.html)

至于现在我只是一个一个地复制像素,这不太明智(尽管我需要让它工作),是时候提高性能了;)

如果有人能以其他方式提出转换,我将不胜感激。

【问题讨论】:

【参考方案1】:

要复制数据,您无需逐个像素地进行。一种方法是(及其标题):

CImage* m_pImg;
cv::Size m_sizeShow;
cv::Mat m_matCVImg;
BITMAPINFO bitInfo;
...
StretchDIBits(m_pImg->GetDC(), 0, 0,
    m_sizeShow.width, m_sizeShow.height, 0, 0,
    m_sizeShow.width, m_sizeShow.height,
    matImgTmp.data, &bitInfo, DIB_RGB_COLORS, SRCCOPY);

查看http://kvy.com.ua/transformation-of-opencv-image-to-mfc-image-in-msvc-project/ 了解分步说明。

【讨论】:

【参考方案2】:

需要进行一些检查,因为 StretchDIBits 要求 m_matCVImg 必须是 DWORD 对齐并且必须是连续的

bool ok1 = m_matCVImg.isContinuous();
bool ok2 = (m_matCVImg.cols % 4) == 0;
ok1:如果您的 m_matCVImg 是 ROI 或稀疏矩阵,则不能直接使用 StretchDIBits ok2:只有当 m_matCVImg 具有模 4 列或 32 位深度时,它才是 DWORD 对齐的

如果您的检查失败,您需要创建一个带有正确 cols 的临时 cv::Mat

 if (!(ok1 && ok2)) 
   // create a new cv::Mat using
   // m_matCVImg.CopyTo(...) or cv::copyMakeBorder(m_matCVImg,...)
 

最后,如果你的 m_matCVImg 是灰度,你必须在 bitInfo.bmiColors 中准备一个平面调色板;

【讨论】:

以上是关于OpenCV 和 TensorRT 之间的数据转换 HWC to CHW的主要内容,如果未能解决你的问题,请参考以下文章

opencv mat和CImage之间的转换

opencv中Mat与IplImage,CVMat类型之间转换

opencv基础opencv和dlib库中rectangle类型之间的转换

将冻结图转换为 TRT 图时 Jetson Nano 上的 TensorRT 错误

TensorFlow Lite、TensorFlow-TRT 和 TensorRT 之间的主要区别是啥?

OpenCV uchar和Mat之间的相互转换