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:
标签:volChl,inputW,TensorRT,HWC,cuda,CHW,inputH,cv,volImg From: https://www.cnblogs.com/penguins/p/17443981.html