NVIDIA 视觉编程接口 (VPI) 是一个软件库,可提供一组计算机视觉和图像处理算法。这些算法的实现在 NVIDIA Jetson 嵌入式计算机或独立 GPU 上可用的不同硬件引擎上得到加速。硬件引擎在 VPI 中被命名为后端。这些后端使您能够卸载可并行处理阶段并通过使用 Jetson 设备固有的可用系统级并行性来加速应用程序。后端是 CPU、CUDA (GPU)、PVA 和 VIC。特定后端引擎的确切可用性取决于部署应用程序的 Jetson 平台。
官方的demo里提供了很多的图像处理算法,用VPI的好处在于,Nvidia又给你造了一个库(泥坑),让你更方便地使用GPU和VIC来做图像处理。直接运行官方的demo就可以实际看效果。
对应上文,我在nvidia 平台,做了一个简单封装,使用VPI来做鱼眼矫正,一张1080P的图像,鱼眼矫正大概耗时1ms,整个处理过程大概3-5ms,基本满足工程的需求,看了一下大部分时间应该是在色彩空间转换上,需要更好的性能需要再深入细调。
void FishEyeCorrect::init(const cv::Mat &kMat, const cv::Mat &dMat, int width, int height) {
// Allocate a dense map.
VPIWarpMap map = {};
map.grid.numHorizRegions = 1;
map.grid.numVertRegions = 1;
map.grid.regionWidth[0] = width;
map.grid.regionHeight[0] = height;
map.grid.horizInterval[0] = 1;
map.grid.vertInterval[0] = 1;
CHECK_STATUS(vpiWarpMapAllocData(&map));
// Initialize the fisheye lens model with the coefficients given by calibration procedure.
VPIFisheyeLensDistortionModel distModel = {};
distModel.mapping = VPI_FISHEYE_EQUIDISTANT;
distModel.k1 = dMat.at<double>(0, 0);
distModel.k2 = dMat.at<double>(0, 1);
distModel.k3 = dMat.at<double>(0, 2);
distModel.k4 = dMat.at<double>(0, 3);
// Fill up the camera intrinsic parameters given by camera calibration procedure.
VPICameraIntrinsic K;
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 3; ++j)
{
K[i][j] = kMat.at<double>(i, j);
}
}
// Camera extrinsics is be identity.
VPICameraExtrinsic X = {};
X[0][0] = X[1][1] = X[2][2] = 1;
// Generate a warp map to undistort an image taken from fisheye lens with
// given parameters calculated above.
vpiWarpMapGenerateFromFisheyeLensDistortionModel(K, X, K, &distModel, &map);
// Create the Remap payload for undistortion given the map generated above.
CHECK_STATUS(vpiCreateRemap(VPI_BACKEND_CUDA, &map, &remap));
// Now that the remap payload is created, we can destroy the warp map.
vpiWarpMapFreeData(&map);
// Create a stream where operations will take place. We're using CUDA
// processing.
CHECK_STATUS(vpiStreamCreate(VPI_BACKEND_CUDA, &stream));
CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_NV12_ER, 0, &tmpIn));
CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_NV12_ER, 0, &tmpOut));
}
void FishEyeCorrect::correctImage(cv::Mat &cvImage) {
if (vimg == nullptr) {
// Now create a VPIImage that wraps it.
CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &vimg));
} else {
CHECK_STATUS(vpiImageSetWrappedOpenCVMat(vimg, cvImage));
}
CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, vimg, tmpIn, NULL));
CHECK_STATUS(vpiSubmitRemap(stream, VPI_BACKEND_CUDA, remap, tmpIn, tmpOut, VPI_INTERP_CATMULL_ROM, VPI_BORDER_ZERO, 0));
CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, tmpOut, vimg, NULL));
CHECK_STATUS(vpiStreamSync(stream));
}