为何相同参数下cv::cuda::remap结果错误而cv::remap结果正确?
问题背景
为借助CUDA加速能力提升视频去畸变处理速度,采用cv::cuda::remap替代cv::remap实现相关功能,两个版本程序均可正常运行,但在相机矩阵、畸变系数、cv::initUndistortRectifyMap()生成的map1和map2完全一致的前提下:
- CPU版本
cv::remap输出的去畸变结果正确 - CUDA版本
cv::cuda::remap输出结果存在明显异常
目前已在OpenCV 4.5.5、4.6.0两个版本中复现该问题。
相关代码片段
CPU版本实现
cv::cuda::GpuMat gpuMat(m_height, m_width, CV_8UC4, (void *)dpFrame); cv::Mat mat; gpuMat.download(mat); cv::remap(mat, mat, m_map1, m_map2, cv::INTER_LINEAR); gpuMat.upload(mat);
CUDA版本实现
cv::cuda::GpuMat gpuMat(m_height, m_width, CV_8UC4, (void *)dpFrame); cv::cuda::remap(gpuMat, gpuMat, m_gpuMap1, m_gpuMap2, cv::INTER_LINEAR);
参数说明:
dpFrame类型为CUdeviceptrm_map1、m_map2由cv::initUndistortRectifyMap计算得到m_gpuMap1、m_gpuMap2为m_map1、m_map2上传至GPU后得到的cv::cuda::GpuMat类型对象
问题原因与解决方案
该异常的核心诱因是映射表上传GPU时的类型不匹配,属于OpenCV CUDA模块使用的高频踩坑点:
- 首先确认
cv::initUndistortRectifyMap生成映射表的输出类型:调用时最后一个参数如果填CV_32FC1,生成的m_map1、m_map2是两个32位浮点单通道矩阵,分别存储x、y方向映射坐标;如果填CV_16SC2,则m_map1是16位整型双通道矩阵存储坐标,m_map2是16位整型单通道矩阵存储插值系数。 - 直接将CPU端map上传到GPU的操作未做类型适配:
cv::cuda::remap要求GPU端映射表必须满足固定格式——要么是两个CV_32FC1类型的GpuMat,要么是第一个映射表为CV_16SC2类型、第二个映射表为CV_16UC1类型。如果CPU端生成的是CV_16SC2+CV_16SC1的组合,直接上传后第二个map类型不符合接口要求,就会出现块状错位、画面撕裂的异常效果,和描述的故障特征完全匹配。 - 不要手动上传CPU端生成的map,优先使用OpenCV提供的
cv::cuda::remap专用映射表转换接口,可彻底规避类型不匹配问题:
// CPU端生成map1、map2的逻辑保持原有实现不变 cv::initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, imageSize, CV_32FC1, m_map1, m_map2); // 调用专用接口生成CUDA remap可用的映射表对象,无需手动执行upload操作 cv::Ptr<cv::cuda::Remap> cuda_remap_op = cv::cuda::createRemap(m_map1, m_map2, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar()); // 帧处理时直接调用apply方法完成映射 cuda_remap_op->apply(gpuMat, gpuMat);
- 如果不使用
createRemap接口、选择手动上传映射表,必须做类型校验与转换:- 用
CV_32FC1类型生成CPU端映射表时,上传后需确认m_gpuMap1、m_gpuMap2均为CV_32FC1类型 - 用
CV_16SC2类型生成CPU端映射表时,上传后必须将第二个map转换为CV_16UC1类型,再传入cv::cuda::remap接口
- 用
- 低优先级排查点:构造GpuMat时直接传入外部
CUdeviceptr,需确认该指针指向显存的步长(stride)与GpuMat的step参数匹配。如果外部分配显存的步长和OpenCV默认对齐步长不一致,也会引发画面错位,但这类错位一般表现为整行偏移,和块状异常的特征不符,优先排查映射表类型问题即可。
内容的提问来源于stack exchange,提问作者Sang
相关产品推荐
相关产品推荐

