NVIDIA JetPack VPI笔记3:VPI中各类Algoritm的介绍与基本示例

Mar 26,2023   101144 words   362 min

Tags: GPU

在本篇笔记中,我们会简单过一遍目前VPI(2.2)支持的一些算法,看看他到底能做些什么,并展示一些官方的示例代码。因为本篇笔记内容比较多,所以并不建议从头到尾全部学习一遍(当然时间充裕也可以),而是作为工具,用到的时候来查一下相关用法。

1.支持的算法

下面列出的每个算法都有对应的官方文档。在官方文档中详细说明了函数的作用以及示例,每一步代码都有注释,很详细。在这里我们只是简单列举。如果想进一步了解,可以直接查阅对应官方文档。首先给出下面这些示例CMakeLists.txt文件。

cmake_minimum_required(VERSION 3.13)
project(vpi_algorithm_examples)

set(CMAKE_CXX_STANDARD 11)

find_package(vpi REQUIRED)
find_package(OpenCV REQUIRED)

add_executable(demo_boxFilter cpp/demo_boxFilter.cpp)
target_link_libraries(demo_boxFilter vpi ${OpenCV_LIBS})

add_executable(demo_bilateralFilter cpp/demo_bilateralFilter.cpp)
target_link_libraries(demo_bilateralFilter vpi ${OpenCV_LIBS})

add_executable(demo_gaussianFilter cpp/demo_gaussianFilter.cpp)
target_link_libraries(demo_gaussianFilter vpi ${OpenCV_LIBS})

add_executable(demo_gaussianPyramid cpp/demo_gaussianPyramid.cpp)
target_link_libraries(demo_gaussianPyramid vpi ${OpenCV_LIBS})

add_executable(demo_laplacianPyramid cpp/demo_laplacianPyramid.cpp)
target_link_libraries(demo_laplacianPyramid vpi ${OpenCV_LIBS})

add_executable(demo_erode cpp/demo_erode.cpp)
target_link_libraries(demo_erode vpi ${OpenCV_LIBS})

add_executable(demo_dilate cpp/demo_dilate.cpp)
target_link_libraries(demo_dilate vpi ${OpenCV_LIBS})

add_executable(demo_convolution cpp/demo_convolution.cpp)
target_link_libraries(demo_convolution vpi ${OpenCV_LIBS})

add_executable(demo_sepConvolution cpp/demo_sepConvolution.cpp)
target_link_libraries(demo_sepConvolution vpi ${OpenCV_LIBS})

add_executable(demo_cvtFormat cpp/demo_cvtFormat.cpp)
target_link_libraries(demo_cvtFormat vpi ${OpenCV_LIBS})

add_executable(demo_rescale cpp/demo_rescale.cpp)
target_link_libraries(demo_rescale vpi ${OpenCV_LIBS})

add_executable(demo_remap cpp/demo_remap.cpp)
target_link_libraries(demo_remap vpi ${OpenCV_LIBS})

add_executable(demo_fft cpp/demo_fft.cpp)
target_link_libraries(demo_fft vpi ${OpenCV_LIBS})

add_executable(demo_ifft cpp/demo_ifft.cpp)
target_link_libraries(demo_ifft vpi ${OpenCV_LIBS})

add_executable(demo_lensDistCorrect cpp/demo_lensDistCorrect.cpp)
target_link_libraries(demo_lensDistCorrect vpi ${OpenCV_LIBS})

add_executable(demo_stereoDisparity cpp/demo_stereoDisparity.cpp)
target_link_libraries(demo_stereoDisparity vpi ${OpenCV_LIBS})

add_executable(demo_harrisDetector cpp/demo_harrisDetector.cpp)
target_link_libraries(demo_harrisDetector vpi ${OpenCV_LIBS})

add_executable(demo_histogram cpp/demo_histogram.cpp)
target_link_libraries(demo_histogram vpi ${OpenCV_LIBS})

add_executable(demo_equalizeHist cpp/demo_equalizeHist.cpp)
target_link_libraries(demo_equalizeHist vpi ${OpenCV_LIBS})

add_executable(demo_minMaxLoc cpp/demo_minMaxLoc.cpp)
target_link_libraries(demo_minMaxLoc vpi ${OpenCV_LIBS})

add_executable(demo_imgFlip cpp/demo_imgFlip.cpp)
target_link_libraries(demo_imgFlip vpi ${OpenCV_LIBS})

add_executable(demo_medianFilter cpp/demo_medianFilter.cpp)
target_link_libraries(demo_medianFilter vpi ${OpenCV_LIBS})

add_executable(demo_FASTDetector cpp/demo_FASTDetector.cpp)
target_link_libraries(demo_FASTDetector vpi ${OpenCV_LIBS})

add_executable(demo_mixChannels cpp/demo_mixChannels.cpp)
target_link_libraries(demo_mixChannels vpi ${OpenCV_LIBS})

add_executable(demo_cannyEdge cpp/demo_cannyEdge.cpp)
target_link_libraries(demo_cannyEdge vpi ${OpenCV_LIBS})

add_executable(demo_ORBDetector cpp/demo_ORBDetector.cpp)
target_link_libraries(demo_ORBDetector vpi ${OpenCV_LIBS})

add_executable(demo_imgStatistics cpp/demo_imgStatistics.cpp)
target_link_libraries(demo_imgStatistics vpi ${OpenCV_LIBS})

add_executable(demo_templateMatching cpp/demo_templateMatching.cpp)
target_link_libraries(demo_templateMatching vpi ${OpenCV_LIBS})

下面的每段代码都经过测试,但个别示例(KLT Feature Tracker、Temporal Noise Reduction、Pyramidal LK Optical Flow、Dense Optical FLow、Background Subtractor)直接从官方文档摘录,代码不完整无法运行。IFFT示例可以运行,但因为输入的数据不对,导致输出结果不正确。之后会进一步研究用法。同时因为代码量比较大,尽管仔细检查了,但也难免有错,欢迎发现问题。为了方便使用,以下所有运行起来的代码都上传到了Github,点击查看,欢迎Start或Fork。

1.1 Box Filter

官方文档是这个。最简单的方形滤波器,是一个低通滤波器,实现对图像的平滑(局部窗口取平均赋值),不支持自定义滤波器内容。直观效果如下。 C++需引用vpi/algo/BoxFilter.h,名字为vpiSubmitBoxFilter(),Python需导入vpi包,名字为.box_filter()

1.1.1 C++接口

C++用法如下,包含初始化、执行和销毁三个阶段:

#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/BoxFilter.h> // BoxFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitBoxFilter(stream, VPI_BACKEND_CUDA, input, output, 5, 5, VPI_BORDER_ZERO);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_boxFilter.jpg", out_mat);

    // step11 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.1.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.box_filter(5, border=vpi.Border.ZERO)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_boxFilter.jpg", outData)
1.2 Bilateral Filter

官方文档是这个。经典的双边滤波器。可以在保留影像边缘信息的同时对噪声进行一定程度的滤除。可以控制滤波器的窗口大小、强度参数。效果如下。 C++需引用vpi/algo/BilateralFilter.h,名字为vpiSubmitBilateralFilter(),Python需导入vpi包,名字为.bilateral_filter()

1.2.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/BilateralFilter.h> // BilateralFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitBilateralFilter(stream, VPI_BACKEND_CUDA, input, output, 7, 50, 1.7, VPI_BORDER_ZERO);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_bilateralFilter.jpg", out_mat);

    // step11 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.2.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.bilateral_filter(5, 7, 50, border=vpi.Border.ZERO)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_bilateralFilter.jpg", outData)
1.3 Gaussian Filter

官方文档是这个。经典高斯滤波器。支持对滤波器大小和强度的设置,效果如下。 C++需引用vpi/algo/GaussionFilter.h,名字为vpiSubmitGaussianFilter(),Python需导入vpi包,名字为.gaussian_filter()

1.3.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/GaussianFilter.h> // GaussianFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitGaussianFilter(stream, VPI_BACKEND_CPU, input, output, 7, 7, 1.7, 1.7, VPI_BORDER_ZERO);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_gaussianFilter.jpg", out_mat);

    // step11 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.3.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.gaussian_filter(7, 1.7, border=vpi.Border.ZERO)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_gaussianFilter.jpg", outData)
1.4 Gaussian Pyramid Generator

官方文档是这个。用于构建高斯金字塔,主要包含金字塔层数和缩放倍数两个核心参数。效果如下。 C++需引用vpi/Pyramid.hvpi/algo/GaussianPyramid.h,名字为vpiSubmitGaussianPyramidGenerator(),Python需导入vpi包,名字为.gaussian_pyramid()

1.4.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/GaussianPyramid.h> // GaussianPyramid头文件
#include <vpi/Pyramid.h>    // VPI Pyramid类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建金字塔对象
    VPIPyramid output;
    vpiPyramidCreate(w, h, type, 4, 0.5, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitGaussianPyramidGenerator(stream, VPI_BACKEND_CPU, input, output, VPI_BORDER_CLAMP);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step9 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiPyramidDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.4.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.gaussian_pyramid(4)
1.5 Laplacian Pyramid Generator

官方文档是这个。用于构建拉普拉斯金字塔。效果如下。 在VPI中拉普拉斯金字塔是通过高斯金字塔上采样然后差分得到的,如下图所示。 C++需引用vpi/Pyramid.hvpi/algo/LaplacianPyramid.h,名字为vpiSubmitLaplacianPyramidGenerator(),Python需导入vpi包,名字为.laplacian_pyramid()

1.5.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/LaplacianPyramid.h> // LaplacianPyramid头文件
#include <vpi/Pyramid.h>    // VPI Pyramid类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建金字塔对象
    VPIPyramid output;
    vpiPyramidCreate(w, h, type, 4, 0.5, 0, &output);
    VPIPyramid gaussianPyr;
    vpiPyramidCreate(w, h, type, 4, 0.5, 0, &gaussianPyr);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitLaplacianPyramidGenerator(stream, VPI_BACKEND_CPU, input, output, gaussianPyr, VPI_BORDER_CLAMP);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step9 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiPyramidDestroy(output);
    vpiPyramidDestroy(gaussianPyr);
    // -------------------------------------------------------------------

    return 0;
}
1.5.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    gaussian = vpi.Pyramid(input.size, input.format, 4)
    with vpi.Backend.CUDA:
        output = input.laplacian_pyramid(4, out_gaussian=gaussian)
1.6 Erode

官方文档是这个。经典腐蚀操作。支持手动修改卷积核大小。效果如下。 C++需引用vpi/algo/MorphologicalFilter.h,名字为vpiSubmitErode(),Python需导入vpi包,名字为.erode()

1.6.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/MorphologicalFilter.h> // MorphologicalFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建卷积核
    int8_t kernel[3 * 3] = {1, 1, 1,
                            1, 1, 1,
                            1, 1, 1};

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitErode(stream, VPI_BACKEND_CPU, input, output, kernel, 3, 3, VPI_BORDER_ZERO);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_erode.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.6.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建卷积核
    kernel = [[1, 1, 1],
              [1, 1, 1],
              [1, 1, 1]]

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.erode(kernel, border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_erode.jpg", outData)
1.7 Dilate

官方文档是这个。经典膨胀操作。支持手动修改卷积核大小。效果如下。 C++需引用vpi/algo/MorphologicalFilter.h,名字为vpiSubmitDilate(),Python需导入vpi包,名字为.dilate()

1.7.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/MorphologicalFilter.h> // MorphologicalFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建卷积核
    int8_t kernel[3 * 3] = {1, 1, 1,
                            1, 1, 1,
                            1, 1, 1};

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitDilate(stream, VPI_BACKEND_CPU, input, output, kernel, 3, 3, VPI_BORDER_ZERO);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_dilate.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.7.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建卷积核
    kernel = [[1, 1, 1],
              [1, 1, 1],
              [1, 1, 1]]

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.dilate(kernel, border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_dilate.jpg", outData)
1.8 Convolution

官方文档是这个。针对常规的卷积操作开发的函数。在VPI中,可以用该函数进行小于5×5的卷积(建议),如果卷积核过大,这个函数性能可能会下降,推荐可分离卷积。效果如下。 C++需引用vpi/algo/Convolution.h,名字为vpiSubmitConvolution(),Python需导入vpi包,名字为.convolution()

1.8.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/Convolution.h> // Convolution头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建卷积核
    float kernel[3 * 3] = {1, 0, -1,
                           0, 0, 0,
                           -1, 0, 1};

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitConvolution(stream, VPI_BACKEND_CPU, input, output, kernel, 3, 3, VPI_BORDER_ZERO);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_conv.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.8.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建卷积核
    kernel = [[1, 0, -1],
              [0, 0, 0],
              [-1, 0, 1]]

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.convolution(kernel, border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_conv.jpg", outData)
1.9 Separable Convolution

官方文档是这个。可分离卷积。就是将常规的二维卷积分离成一个水平和一个竖直的分量。因为二维卷积本质上是一个矩阵,而一个m×n的矩阵可以由一个m×1的矩阵和一个1×n的矩阵相乘得到。根据官方文档,相比于上面提到的Convolution。可分离卷积在面对大尺度的卷积核时性能会更好。效果如下。 C++需引用vpi/algo/Convolution.h,名字为vpiSubmitSeparableConvolution(),Python需导入vpi包,名字为.convolution()

1.9.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/Convolution.h> // Convolution头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建卷积核
    float sobel_row[7] = {-1, -5, -6, 0, +6, +5, +1};
    float sobel_col[7] = {1 / 64.f, 6 / 64.f, 15 / 64.f, 20 / 64.f, 15 / 64.f, 6 / 64.f, 1 / 64.f};

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitSeparableConvolution(stream, VPI_BACKEND_CUDA, input, output, sobel_row, 7, sobel_col, 7, VPI_BORDER_ZERO);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_sepConv.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.9.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建卷积核
    sobel_row = [-1, -5, -6, 0, +6, +5, +1];
    sobel_col = [1 / 64.0, 6 / 64.0, 15 / 64.0, 20 / 64.0, 15 / 64.0, 6 / 64.0, 1 / 64.0]

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.convolution(kernel_x=sobel_row, kernel_y=sobel_col, border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_sepConv.jpg", outData)
1.10 Convert Image Format

官方文档是这个。这个函数主要的作用是转换影像的格式。比如将一个无符号char类型(0-255)的影像转换为有符号short类型(-32768到32767)。此外,函数支持灰度到彩色、灰度到灰度、彩色到灰度的转换。效果如下。 C++需引用vpi/algo/ConvertImageFormat.h,名字为vpiSubmitConvertImageFormat(),Python需导入vpi包,名字为.convert()

1.10.1 常见影像格式

完整格式和详细说明参考上面提到的官方文档。这里简单列举。对于彩色空间,常见格式有:

  • YUV颜色空间
    • 简化量化范围
      • VPI_IMAGE_FORMAT_NV12 (Y:16-235, Cb&Cr:16-240(分辨率为luma plane的一半))
      • VPI_IMAGE_FORMAT_NV12_BL (Y:16-235, Cb&Cr:0-255(分辨率为luma plane的一半))
      • VPI_IMAGE_FORMAT_NV24 (Y:16-235, Cb&Cr:16-240(分辨率与luma plane相同))
      • VPI_IMAGE_FORMAT_NV24_BL (Y:16-235, Cb&Cr:0-255(分辨率与luma plane相同))
      • VPI_IMAGE_FORMAT_YUYV
      • VPI_IMAGE_FORMAT_YUYV_BL
      • VPI_IMAGE_FORMAT_UYVY
      • VPI_IMAGE_FORMAT_UYVY_BL
    • 扩展量化范围
      • VPI_IMAGE_FORMAT_NV12_ER (Y:0-255, Cb&Cr:0-255(分辨率为luma plane的一半))
      • VPI_IMAGE_FORMAT_NV12_ER_BL (Y:0-255, Cb&Cr:0-255(分辨率为luma plane的一半))
      • VPI_IMAGE_FORMAT_NV24_ER (Y:0-255, Cb&Cr:0-255(分辨率与luma plane相同))
      • VPI_IMAGE_FORMAT_NV24_ER_BL (Y:0-255, Cb&Cr:0-255(分辨率与luma plane相同))
      • VPI_IMAGE_FORMAT_YUYV_ER
      • VPI_IMAGE_FORMAT_YUYV_ER_BL
      • VPI_IMAGE_FORMAT_UYVY_ER
      • VPI_IMAGE_FORMAT_UYVY_ER_BL
  • RGB颜色空间
    • 没有alpha透明通道
      • VPI_IMAGE_FORMAT_RGB8
      • VPI_IMAGE_FORMAT_BGR8
    • 有alpha透明通道
      • VPI_IMAGE_FORMAT_RGBA8
      • VPI_IMAGE_FORMAT_BGRA8

对于单通道灰度影像,常见格式有:

  • luma格式
    • 简化量化范围
      • VPI_IMAGE_FORMAT_Y8 (Y:16-235)
      • VPI_IMAGE_FORMAT_Y8_BL (Y:16-235)
    • 扩展量化范围
      • VPI_IMAGE_FORMAT_Y8_ER (Y:0-255)
      • VPI_IMAGE_FORMAT_Y8_ER_BL (Y:0-255)
      • VPI_IMAGE_FORMAT_Y16_ER (Y:0-65535)
      • VPI_IMAGE_FORMAT_Y16_ER_BL (Y:0-65535)
  • 灰度整型格式
    • VPI_IMAGE_FORMAT_U8 (8-bit unsigned integer)
    • VPI_IMAGE_FORMAT_S8 (8-bit signed integer)
    • VPI_IMAGE_FORMAT_U16 (16-bit unsigned integer)
    • VPI_IMAGE_FORMAT_S16 (16-bit signed integer)
  • 灰度浮点型格式
    • VPI_IMAGE_FORMAT_F32 (32-bit floating point)

在CPU和CUDA后端上,可用的转换见下表。 更详细的转换pipeline见官方文档,此处不再赘述。

1.10.2 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/ConvertImageFormat.h> // ConvertImageFormat头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_COLOR);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U8, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);

    // step7 创建转换相关参数
    VPIConvertImageFormatParams cvtParams;
    vpiInitConvertImageFormatParams(&cvtParams);
    cvtParams.policy = VPI_CONVERSION_CLAMP;
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CPU, input, output, &cvtParams);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_cvt.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.10.3 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_COLOR)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.convert(vpi.Format.U8)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_cvt.jpg", outData)
1.11 Rescale

官方文档是这个。实现对于影像的缩放。效果如下。 C++需引用vpi/algo/Rescale.h,名字为vpiSubmitRescale(),Python需导入vpi包,名字为.rescale()

1.11.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/Rescale.h> // Rescale头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w * 2 / 3.0f, h * 3 / 2.0f, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 将操作提交到流中执行
    vpiSubmitRescale(stream, VPI_BACKEND_CUDA, input, output, VPI_INTERP_LINEAR, VPI_BORDER_ZERO, 0);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_rescale.jpg", out_mat);

    // step11 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.11.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.rescale((input.width * 2 // 3, input.height * 3 // 2), interp=vpi.Interp.LINEAR,
                               border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_rescale.jpg", outData)
1.12 Remap

官方文档是这个。可以实现图像间的变换、镜头畸变校正等变换。效果如下。 C++需引用vpi/WarpMap.hvpi/algo/Remap.h,名字为vpiCreateRemap()vpiSubmitRemap(),Python需导入vpi包,名字为vpi.WarpMap().remap()

1.12.1 实现原理

对于此类的变换,核心就是构建一个统一的变换关系,然后将原图中的像素一一解算到新的影像上即可。在VPI中,我们可以实现稠密、稀疏(等间隔)、稀疏(不等间隔)的变换,如下。 在VPI中,mapping可以分为两个步骤。首先是基于稀疏映射图通过双线性内插生成稠密图,第二步则是基于这个稠密图对原图中的像素进行采样。

1.12.2 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/Remap.h> // Remap头文件
#include <vpi/WarpMap.h>    // VPI WarpMap类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;

    // step6 创建稠密的Warp Map
    VPIWarpMap map;
    memset(&map, 0, sizeof(map));
    map.grid.numHorizRegions = 1;
    map.grid.numVertRegions = 1;
    map.grid.regionWidth[0] = w;
    map.grid.regionHeight[0] = h;
    map.grid.horizInterval[0] = 1;
    map.grid.vertInterval[0] = 1;
    vpiWarpMapAllocData(&map);

    // step7 创建小星球特效
    vpiWarpMapGenerateIdentity(&map);
    int i;
    for (i = 0; i < map.numVertPoints; ++i) {
        VPIKeypointF32 *row = (VPIKeypointF32 *) ((uint8_t *) map.keypoints + map.pitchBytes * i);
        int j;
        for (j = 0; j < map.numHorizPoints; ++j) {
            float x = row[j].x - w / 2.0f;
            float y = row[j].y - h / 2.0f;

            const float R = h / 8.0f; /* planet radius */

            const float r = sqrtf(x * x + y * y);

            float theta = M_PI + atan2f(y, x);
            float phi = M_PI / 2 - 2 * atan2f(r, 2 * R);

            row[j].x = fmod((theta + M_PI) / (2 * M_PI) * (w - 1), w - 1);
            row[j].y = (phi + M_PI / 2) / M_PI * (h - 1);
        }
    }

    // step8 创建算法需要的payload
    VPIPayload warp;
    vpiCreateRemap(VPI_BACKEND_CUDA, &map, &warp);

    // step9 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step10 将操作提交到流中执行
    vpiSubmitRemap(stream, VPI_BACKEND_CUDA, warp, input, output, VPI_INTERP_LINEAR, VPI_BORDER_ZERO, 0);

    // step11 等待所有操作执行完成
    vpiStreamSync(stream);

    // step12 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step13 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_remap.jpg", out_mat);

    // step14 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step15 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(warp);
    vpiWarpMapFreeData(&map);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.12.3 Python接口
import cv2
import numpy as np
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建warp map
    warp = vpi.WarpMap(vpi.WarpGrid(input.size))
    wx, wy = np.asarray(warp).transpose(2, 1, 0)

    x = wx - input.width / 2
    y = wy - input.height / 2

    R = input.height / 8  # planet radius
    r = np.sqrt(x * x + y * y)

    theta = np.pi + np.arctan2(y, x)
    phi = np.pi / 2 - 2 * np.arctan2(r, 2 * R)

    wx[:] = np.fmod((theta + np.pi) / (2 * np.pi) * (input.width - 1), input.width - 1)
    wy[:] = (phi + np.pi / 2) / np.pi * (input.height - 1)

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.remap(warp)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_remap.jpg", outData)
1.13 Perspective Warp

官方文档是这个。该函数可以实现投影变换。效果如下。 C++需引用vpi/algo/PerspectiveWarp.h,名字为vpiSubmitPerspectiveWarp(),Python需导入vpi包,名字为.perspwarp()

1.13.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/PerspectiveWarp.h> // PerspectiveWarp头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);

    // step7 创建变换
    const VPIPerspectiveTransform xform =
            {
                    {0.5386,  0.1419, -74},
                    {-0.4399, 0.8662, 291.5},
                    {-0.0005, 0.0003, 1}
            };
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 将操作提交到流中执行
    vpiSubmitPerspectiveWarp(stream, VPI_BACKEND_CPU, input, xform, output, NULL, VPI_INTERP_LINEAR, VPI_BORDER_ZERO,
                             0);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_persp.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.13.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "./test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 创建变换
    xform = [[0.5386, 0.1419, -74],
             [-0.4399, 0.8662, 291.5],
             [-0.0005, 0.0003, 1]]

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.perspwarp(xform)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("./test-data/out_persp.jpg", outData)
1.14 FFT

官方文档是这个。经典傅立叶变换。VPI的实现细节可以看上面的这个文档,此处不再赘述。效果如下。 C++需引用vpi/algo/FFT.h,名字为vpiCreateFFT()vpiSubmitFFT(),Python需导入vpi包,名字为.fft()

1.14.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/ConvertImageFormat.h> // ConvertImageFormat头文件
#include <vpi/algo/FFT.h>   // FFT头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t width, height;
    vpiImageGetSize(input, &width, &height);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建Float32类型的输入影像
    VPIImage inputF32;
    vpiImageCreate(width, height, VPI_IMAGE_FORMAT_F32, 0, &inputF32);

    // step6 创建用于可视化的谱段的影像
    float *tmpBuffer = (float *) malloc(width * 2 * height * sizeof(float));

    // step7 创建输出谱段影像
    VPIImage spectrum;
    vpiImageCreate(width / 2 + 1, height, VPI_IMAGE_FORMAT_2F32, 0, &spectrum);

    // step8 创建FFT对应Payload
    VPIPayload fft;
    vpiCreateFFT(VPI_BACKEND_CUDA, width, height, VPI_IMAGE_FORMAT_F32, VPI_IMAGE_FORMAT_2F32, &fft);

    // step9 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step10 将输入的Uint8类型影像转化为Float32类型
    vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, input, inputF32, NULL);

    // step11 执行傅立叶变换
    vpiSubmitFFT(stream, VPI_BACKEND_CUDA, fft, inputF32, spectrum, 0);

    // step12 等待所有操作执行完成
    vpiStreamSync(stream);

    // step13 创建锁定对象,取出数据到tmpBuffer
    VPIImageData data;
    vpiImageLockData(spectrum, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &data);

    // step13.1 可视化谱段处理
    // Fills the right half of the complex data with the missing values. The left half is copied directly from VPI's output.
    /* make width/height even*/
    width = width & -2;
    height = height & -2;

    /* center pixel coordinate */
    int cx = width / 2;
    int cy = height / 2;

    /* Image data is in host-accessible pitch-linear layout. */
    assert(data.bufferType == VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR);
    VPIImageBufferPitchLinear *dataPitch = &data.buffer.pitch;

    int i, j;
    for (i = 0; i < (int) height; ++i) {
        for (j = 0; j < (int) width; ++j) {
            float re, im;
            if (j < cx) {
                const float *pix =
                        (const float *) ((const char *) dataPitch->planes[0].data +
                                         i * dataPitch->planes[0].pitchBytes) +
                        j * 2;
                re = pix[0];
                im = pix[1];
            } else {
                const float *pix = (const float *) ((const char *) dataPitch->planes[0].data +
                                                    ((height - i) % height) * dataPitch->planes[0].pitchBytes) +
                                   ((width - j) % width) * 2;
                /* complex conjugate */
                re = pix[0];
                im = -pix[1];
            }

            tmpBuffer[i * (width * 2) + j * 2] = re;
            tmpBuffer[i * (width * 2) + j * 2 + 1] = im;
        }
    }

    // step13.2 Convert the complex frequencies into normalized log-magnitude
    float min = FLT_MAX, max = -FLT_MAX;

    for (i = 0; i < (int) height; ++i) {
        for (j = 0; j < (int) width; ++j) {
            float re, im;
            re = tmpBuffer[i * (width * 2) + j * 2];
            im = tmpBuffer[i * (width * 2) + j * 2 + 1];

            float mag = logf(sqrtf(re * re + im * im) + 1);
            tmpBuffer[i * width + j] = mag;

            min = mag < min ? mag : min;
            max = mag > max ? mag : max;
        }
    }

    for (i = 0; i < (int) height; ++i) {
        for (j = 0; j < (int) width; ++j) {
            tmpBuffer[i * width + j] = (tmpBuffer[i * width + j] - min) / (max - min);
        }
    }

    // step13.3 Shift the spectrum so that DC is at center
    for (i = 0; i < (int) height; ++i) {
        for (j = 0; j < (int) cx; ++j) {
            float a = tmpBuffer[i * width + j];

            /* quadrant 0? */
            if (i < cy) {
                /* swap it with quadrant 3 */
                tmpBuffer[i * width + j] = tmpBuffer[(i + cy) * width + (j + cx)];
                tmpBuffer[(i + cy) * width + (j + cx)] = a;
            }
                /* quadrant 2? */
            else {
                /* swap it with quadrant 1*/
                tmpBuffer[i * width + j] = tmpBuffer[(i - cy) * width + (j + cx)];
                tmpBuffer[(i - cy) * width + (j + cx)] = a;
            }
        }
    }

    // step14 将取出的数据转换为OpenCV格式并保存
    // tmpBuffer波动范围在0到1之间
    Mat out_mat(height, width, CV_32F, tmpBuffer);
    out_mat = out_mat * 255;
    imwrite("../test-data/out_fft.jpg", out_mat);

    // step15 解除对对象的锁定
    vpiImageUnlock(spectrum);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step16 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(inputF32);
    vpiImageDestroy(spectrum);
    free(tmpBuffer);
    // -------------------------------------------------------------------

    return 0;
}
1.14.2 Python接口
import cv2
import vpi  # 导入VPI
import numpy as np

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.convert(vpi.Format.F32).fft()

    # step5 变换后的后处理,以方便可视化
    hfreq = output.cpu().view(dtype=np.complex64).squeeze(2)

    if input.width % 2 == 0:
        wpad = input.width // 2 - 1
        padmode = 'reflect'
    else:
        wpad = input.width // 2
        padmode = 'symmetric'

    freq = np.pad(hfreq, ((0, 0), (0, wpad)), mode=padmode)
    freq[:, hfreq.shape[1]:] = np.conj(freq[:, hfreq.shape[1]:])
    freq[1:, hfreq.shape[1]:] = freq[1:, hfreq.shape[1]:][::-1]

    lmag = np.log(1 + np.absolute(freq))
    spectrum = np.fft.fftshift(lmag)

    # step6 结果输出
    max_v = np.max(spectrum)
    min_v = np.min(spectrum)
    scale = 255 / (max_v - min_v)
    spectrum = scale * (spectrum - min_v)
    cv2.imwrite("../test-data/out_fft.jpg", spectrum)
1.15 Inverse FFT

官方文档是这个。用于实现经典逆傅立叶变换。效果如下。 C++需引用vpi/algo/FFT.h,名字为vpiCreateIFFT()vpiSubmitIFFT(),Python需导入vpi包,名字为.irfft()

1.15.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/FFT.h>   // IFFT头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/out_fft.jpg";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    Mat img_norm;
    img.convertTo(img_norm, CV_32FC2);
    img_norm = img_norm / 255;

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img_norm, 0, &input);

    // step3 获取影像大小
    int32_t width, height;
    vpiImageGetSize(input, &width, &height);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(width, height, VPI_IMAGE_FORMAT_F32, 0, &output);

    // step6 创建IFFT对应Payload
    VPIPayload ifft;
    vpiCreateIFFT(VPI_BACKEND_CUDA, width, height, VPI_IMAGE_FORMAT_2F32, VPI_IMAGE_FORMAT_2F32, &ifft);

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 执行操作
    vpiSubmitIFFT(stream, VPI_BACKEND_CUDA, ifft, input, output, 0);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据到tmpBuffer
    VPIImageData data;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &data);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(data, &out_mat);
    imwrite("../test-data/out_ifft.jpg", out_mat);

    // step12 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(ifft);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.15.2 Python接口
import cv2
import vpi  # 导入VPI

if __name__ == '__main__':
    img_path = "../test-data/out_ifft.jpg"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.irfft()
1.16 Lens Distortion Correction

官方文档是这个。主要用于镜头的畸变矫正。目前在VPI中主要支持两类畸变:多项式畸变(Polynomial)和鱼眼畸变(Fisheye)。实现细节见上面的文档。效果如下。 C++需引用vpi/algo/LensDistortionModels.hvpi/algo/Remap.h,名字为vpiWarpMapGenerateFromFisheyeLensDistortionModel()vpiWarpMapGenerateFromPolynomialLensDistortionModel(),Python需导入vpi包,名字为.fisheye_correction()

1.16.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/LensDistortionModels.h>   // DistortionModel头文件
#include <vpi/algo/Remap.h>   // Remap头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img, 0, &input);

    // step3 获取影像大小
    int32_t width, height;
    vpiImageGetSize(input, &width, &height);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出影像
    VPIImage output;
    vpiImageCreate(width, height, type, 0, &output);

    // step6 构造校正变换关系
    VPIWarpMap map;
    memset(&map, 0, sizeof(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;
    vpiWarpMapAllocData(&map);

    // step7 构造校正模型
    VPIFisheyeLensDistortionModel fisheye;
    memset(&fisheye, 0, sizeof(fisheye));
    fisheye.mapping = VPI_FISHEYE_EQUIDISTANT;
    fisheye.k1 = -0.126;
    fisheye.k2 = 0.004;
    fisheye.k3 = 0;
    fisheye.k4 = 0;

    float sensorWidth = 22.2; /* APS-C sensor */
    float focalLength = 7.5;
    float f = focalLength * width / sensorWidth;
    const VPICameraIntrinsic K =
            {
                    {f, 0, static_cast<float>(width / 2.0)},
                    {0, f, static_cast<float>(height / 2.0)}
            };
    const VPICameraExtrinsic X =
            {
                    {1, 0, 0, 0},
                    {0, 1, 0, 0},
                    {0, 0, 1, 0}
            };

    vpiWarpMapGenerateFromFisheyeLensDistortionModel(K, X, K, &fisheye, &map);

    // step8 创建校正对应Payload
    VPIPayload warp;
    vpiCreateRemap(VPI_BACKEND_CUDA, &map, &warp);

    // step9 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step10 执行操作
    vpiSubmitRemap(stream, VPI_BACKEND_CUDA, warp, input, output, VPI_INTERP_CATMULL_ROM, VPI_BORDER_ZERO, 0);

    // step11 等待所有操作执行完成
    vpiStreamSync(stream);

    // step12 创建锁定对象,取出数据到tmpBuffer
    VPIImageData data;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &data);

    // step13 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(data, &out_mat);
    imwrite("../test-data/out_dist.jpg", out_mat);

    // step14 解除对对象的锁定
    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step15 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(warp);
    vpiWarpMapFreeData(&map);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.16.2 Python接口
import cv2
import vpi  # 导入VPI包
import numpy as np

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img)

    # step3 构造变换关系
    grid = vpi.WarpGrid(input.size)
    sensorWidth = 22.2  # APS-C sensor
    focalLength = 7.5
    f = focalLength * input.width / sensorWidth

    K = [[f, 0, input.width / 2],
         [0, f, input.height / 2]]
    X = np.eye(3, 4)

    warp = vpi.WarpMap.fisheye_correction(grid, K=K, X=X,
                                          mapping=vpi.FisheyeMapping.EQUIDISTANT,
                                          coeffs=[-0.126, 0.004])

    # step4 执行操作
    with vpi.Backend.CUDA:
        output = input.remap(warp, interp=vpi.Interp.CATMULL_ROM, border=vpi.Border.ZERO)

    # step5 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_dist.jpg", outData)
1.17 Stereo Disparity Estimator

官方文档是这个。可以实现双目视差图估计。效果如下。 C++需引用vpi/algo/StereoDisparity.h,名字为vpiCreateStereoDisparityEstimator(),Python需导入vpi包,名字为vpi.stereodisp()

1.17.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/StereoDisparity.h>   // StereoDisparity头文件
#include <vpi/algo/ConvertImageFormat.h>    // 影像类型转换头文件

using namespace cv;
using namespace std;

int main() {
    string img_path1 = "../test-data/chair_stereo_left_grayscale.png";
    string img_path2 = "../test-data/chair_stereo_right_grayscale.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path1, IMREAD_GRAYSCALE);
    Mat img2 = imread(img_path2, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage left, right;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &left);
    vpiImageCreateWrapperOpenCVMat(img2, 0, &right);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(left, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(left, &type);

    // step5 创建输出影像
    VPIImage disparity;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_S16, 0, &disparity);

    VPIImage confidence;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U16, 0, &confidence);

    VPIImage display;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U8, 0, &display);

    // step6 视差估计参数
    VPIStereoDisparityEstimatorParams params;
    vpiInitStereoDisparityEstimatorParams(&params);
    params.windowSize = 5;
    params.maxDisparity = 64;

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);

    // step8 创建对应Payload
    VPIPayload stereo;
    vpiCreateStereoDisparityEstimator(VPI_BACKEND_CUDA, 480, 270, VPI_IMAGE_FORMAT_U16, NULL, &stereo);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step9 执行操作
    vpiSubmitStereoDisparityEstimator(stream, VPI_BACKEND_CUDA, stereo, left, right, disparity, confidence, &params);

    VPIConvertImageFormatParams cvtParams;
    vpiInitConvertImageFormatParams(&cvtParams);
    cvtParams.scale = 1.0f / (32 * params.maxDisparity) * 255;

    vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, disparity, display, &cvtParams);

    // step10 等待所有操作执行完成
    vpiStreamSync(stream);

    // step11 创建锁定对象,取出数据
    VPIImageData data;
    vpiImageLockData(display, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &data);

    // step12 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(data, &out_mat);
    imwrite("../test-data/out_disparity.jpg", out_mat);

    // step13 解除对对象的锁定
    vpiImageUnlock(display);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step14 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(stereo);
    vpiImageDestroy(left);
    vpiImageDestroy(right);
    vpiImageDestroy(display);
    vpiImageDestroy(disparity);
    vpiImageDestroy(confidence);
    // -------------------------------------------------------------------

    return 0;
}
1.17.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path1 = "../test-data/chair_stereo_left_grayscale.png"
    img_path2 = "../test-data/chair_stereo_right_grayscale.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path1, cv2.IMREAD_GRAYSCALE)
    img2 = cv2.imread(img_path2, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    left = vpi.asimage(img1)
    right = vpi.asimage(img2)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = vpi.stereodisp(left, right, window=5, maxdisp=64) \
            .convert(vpi.Format.U8, scale=1.0 / (32 * 64) * 255)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_disparity.jpg", outData)
1.18 KLT Feature Tracker

官方文档是这个。用于实现KLT特征光流跟踪。效果如下。 C++需引用vpi/algo/KLTFeatureTracker.h,名字为vpiSubmitKLTFeatureTracker(),Python需导入vpi包,名字为vpi.KLTFeatureTracker()

1.18.1 C++接口
#include <vpi/algo/KLTFeatureTracker.h>

int frame_count  = /*... */;
VPIImage *frames = /* ... */;

int bbox_count         = /* ... */;
VPIBoundingBox *bboxes = /* ... */;

VPIKLTTrackedBoundingBox tracked_bboxes[128];

int b;
for (b = 0; b < bbox_count; ++b)
{
    tracked_bboxes[b].bbox           = bboxes[b];
    tracked_bboxes[b].trackingStatus = 0; /* valid tracking */
    tracked_bboxes[b].templateStatus = 1; /* must update */
}

VPIArrayData data_bboxes;
memset(&data_bboxes, 0, sizeof(data_bboxes));
data_bboxes.bufferType             = VPI_ARRAY_BUFFER_HOST_AOS;
data_bboxes.buffer.aos.type        = VPI_ARRAY_TYPE_KLT_TRACKED_BOUNDING_BOX;
data_bboxes.buffer.aos.capacity    = 128;
data_bboxes.buffer.aos.sizePointer = &bbox_count;
data_bboxes.buffer.aos.data        = tracked_bboxes;

VPIArray inputBoxList;
vpiArrayCreateWrapper(&data_bboxes, 0, &inputBoxList);

VPIHomographyTransform2D preds[128];
int i;
for (i = 0; i < bbox_count; ++i)
{
    VPIHomographyTransform2D *xform = preds + i;

    /* Identity transform. */
    memset(xform, 0, sizeof(*xform));
    xform->mat3[0][0] = 1;
    xform->mat3[1][1] = 1;
    xform->mat3[2][2] = 1;
}

VPIArrayData data_preds;
memset(&data_preds, 0, sizeof(data_preds));
data_preds.bufferType             = VPI_ARRAY_BUFFER_HOST_AOS;
data_preds.buffer.aos.type        = VPI_ARRAY_TYPE_HOMOGRAPHY_TRANSFORM_2D;
data_preds.buffer.aos.capacity    = 128;
int32_t data_preds_size           = bbox_count;
data_preds.buffer.aos.sizePointer = &data_preds_size;
data_preds.buffer.aos.data        = preds;

VPIArray inputPredList;
vpiArrayCreateWrapper(&data_preds, 0, &inputPredList);

VPIImageFormat imgFormat;
vpiImageGetFormat(frames[0], &imgFormat);

int width, height;
vpiImageGetSize(frames[0], &width, &height);

VPIPayload klt;
vpiCreateKLTFeatureTracker(VPI_BACKEND_CUDA, width, height, imgFormat, NULL, &klt);

VPIKLTFeatureTrackerParams params;
vpiInitKLTFeatureTrackerParams(&params);

VPIArray outputBoxList;
vpiArrayCreate(128, VPI_ARRAY_TYPE_KLT_TRACKED_BOUNDING_BOX, 0, &outputBoxList);

VPIArray outputEstimList;
vpiArrayCreate(128, VPI_ARRAY_TYPE_HOMOGRAPHY_TRANSFORM_2D, 0, &outputEstimList);

VPIStream stream;
vpiStreamCreate(0, &stream);

for (int idframe = 1; idframe < frame_count; ++idframe)
{
    VPIImage imgTemplate  = frames[idframe - 1];
    VPIImage imgReference = frames[idframe];

    VPI_CHECK_STATUS(vpiSubmitKLTFeatureTracker(stream, VPI_BACKEND_CUDA, klt, imgTemplate, inputBoxList,
                                                inputPredList, imgReference, outputBoxList, outputEstimList,
                                                &params));

    vpiStreamSync(stream);

    VPIArrayData updatedBBoxData;
    vpiArrayLockData(outputBoxList, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &updatedBBoxData);

    VPIArrayData estimData;
    vpiArrayLockData(outputEstimList, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &estimData);

    VPIKLTTrackedBoundingBox *updated_bbox = (VPIKLTTrackedBoundingBox *)updatedBBoxData.buffer.aos.data;
    VPIHomographyTransform2D *estim        = (VPIHomographyTransform2D *)estimData.buffer.aos.data;

    vpiArrayLock(inputBoxList, VPI_LOCK_READ_WRITE);
    vpiArrayLock(inputPredList, VPI_LOCK_READ_WRITE);

    int b;
    for (b = 0; b < bbox_count; ++b)
    {
        tracked_bboxes[b].trackingStatus = updated_bbox[b].trackingStatus;
        tracked_bboxes[b].templateStatus = updated_bbox[b].templateStatus;

        if (updated_bbox[b].trackingStatus)
        {
            continue;
        }
        if (updated_bbox[b].templateStatus)
        {
            tracked_bboxes[b] = updated_bbox[b];
            memset(&preds[b], 0, sizeof(preds[b]));
            preds[b].mat3[0][0] = 1;
            preds[b].mat3[1][1] = 1;
            preds[b].mat3[2][2] = 1;
        }
        else
        {
            preds[b] = estim[b];
        }
    }
    vpiArrayUnlock(inputBoxList);
    vpiArrayUnlock(inputPredList);

    vpiArrayUnlock(outputBoxList);
    vpiArrayUnlock(outputEstimList);
}
vpiStreamDestroy(stream);
vpiPayloadDestroy(klt);
vpiArrayDestroy(inputBoxList);
vpiArrayDestroy(inputPredList);
vpiArrayDestroy(outputBoxList);
vpiArrayDestroy(outputEstimList);
1.18.2 Python接口
import vpi

# This function reads the results of the KLT tracker (output bounding boxes and estimations) and updates the input bounding boxes and predictions for the next iteration.
def customUpdate(inBoxes, inPreds, outBoxes, outEstim):
    with inBoxes.lock_cpu() as inBoxes_cpu, inPreds.lock_cpu() as inPreds_cpu, \
         outBoxes.rlock_cpu() as outBoxes_cpu, outEstim.rlock_cpu() as outEstim_cpu:
        inBoxes_ = inBoxes_cpu.view(np.recarray)
        inPreds_ = inPreds_cpu.view(np.recarray)
        outBoxes_ = outBoxes_cpu.view(np.recarray)
        outEstim_ = outEstim_cpu.view(np.recarray)
 
        for i in range(outBoxes.size):
            # If the track status of a bounding box is lost, it assigns lost to the corresponding input bouding box.
            if outBoxes_[i].tracking_status == vpi.KLTTrackStatus.LOST:
                inBoxes_[i].tracking_status = vpi.KLTTrackStatus.LOST
                continue
 
            # If the template status is update needed, it updates the input bounding box with the corresponding output, making its prediction the identity matrix (fixing the bounding box).
            if outBoxes_[i].template_status == vpi.KLTTemplateStatus.UPDATE_NEEDED:
                inBoxes_[i] = outBoxes_[i]
                inPreds_[i] = np.eye(3)
            else:
                # If the update is not needed, just update the input prediction by the corresponding output estimation.
                inBoxes_[i].template_status = vpi.KLTTemplateStatus.UPDATE_NOT_NEEDED
                inPreds_[i] = outEstim_[i]

def convertFrameImage(inputFrame):
    if inputFrame.ndim == 3 and inputFrame.shape[2] == 3:
        grayFrame = cv2.cvtColor(inputFrame, cv2.COLOR_BGR2GRAY)
    else:
        grayFrame = inputFrame
    grayImage = vpi.asimage(grayFrame.copy())
    return grayFrame, grayImage

inBoxes = vpi.Array(totalNumBoxes, vpi.Type.KLT_TRACKED_BOUNDING_BOX)

validFrame, cvFrame = inVideo.read()
if not validFrame:
    print("Error reading first input frame", file=sys.stderr)
    exit(1)
 
# Convert OpenCV frame to gray returning also the VPI image
cvGray, imgTemplate = convertFrameImage(cvFrame)

klt = vpi.KLTFeatureTracker(imgTemplate, inBoxes, backend=vpi.Backend.CUDA)
 
inPreds = klt.in_predictions()

while validFrame:
    if curFrame in allBoxes:
        klt.add_boxes(allBoxes[curFrame])

    curFrame += 1
    validFrame, cvFrame = inVideo.read()
    if not validFrame:
        break
 
    cvGray, imgReference = convertFrameImage(cvFrame)

outBoxes = klt(imgReference, update=customUpdate)
1.19 Harris Corner Detector

官方文档是这个。用于实现Harris角点的提取。效果如下。 C++需引用vpi/algo/HarrisCorners.h,名字为vpiSubmitHarrisCornerDetector(),Python需导入vpi包,名字为vpi.harriscorners()

1.19.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <opencv2/imgcodecs.hpp>
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/HarrisCorners.h>   // HarrisCorners头文件
#include <vpi/Array.h>  // VPI Array类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出keypoint
    VPIArray keypoints;
    vpiArrayCreate(8192, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &keypoints);

    VPIArray scores;
    vpiArrayCreate(8192, VPI_ARRAY_TYPE_U32, 0, &scores);

    // step6 创建对应Payload
    VPIPayload harris;
    vpiCreateHarrisCornerDetector(VPI_BACKEND_CUDA, w, h, &harris);

    // step7 创建Harris参数
    VPIHarrisCornerDetectorParams params;
    vpiInitHarrisCornerDetectorParams(&params);
    params.sensitivity = 0.01;

    // step8 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step9 执行操作
    vpiSubmitHarrisCornerDetector(stream, 0, harris, input, keypoints, scores, &params);

    // step10 等待所有操作执行完成
    vpiStreamSync(stream);

    // step11 创建锁定对象,取出数据
    VPIArrayData data;
    vpiArrayLockData(keypoints,VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &data);

    // step12 解除对对象的锁定
    vpiArrayUnlock(keypoints);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step13 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(harris);
    vpiImageDestroy(input);
    vpiArrayDestroy(keypoints);
    vpiArrayDestroy(scores);
    // -------------------------------------------------------------------

    return 0;
}
1.19.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CUDA:
        keypoints, scores = input.harriscorners(sensitivity=0.01)

    # step4 结果输出
    with keypoints.rlock_cpu() as keyPointData:
        print(keyPointData)

    with scores.rlock_cpu() as scoreData:
        print(scoreData)
1.20 Temporal Noise Reduction

官方文档是这个。用于实现时序噪声的去除。根据文档描述,VPI的这个函数更适合于热噪声(高斯分布)和拍摄噪声(泊松分布),对于其它类型的噪声效果可能不是很明显。效果如下。 C++需引用vpi/algo/TemporalNoiseReduction.h,名字为vpiSubmitTemporalNoiseReduction(),Python需导入vpi包,名字为vpi.TemporalNoiseReduction()

在VPI中,提供了三个版本的函数,分别适用于不同的需求:

  • VPI_TNR_V1:Offers basic noise reduction that works well when noise levels aren’t too high. Lighting conditions and noise reduction strength are fixed and can’t be configured, but in general it provides good speed.
  • VPI_TNR_V2:Offers improved noise reduction, and can be configured for a particular lighting condition. Still provides good speed.
  • VPI_TNR_V3:Offers best noise reduction quality and configurability, in exchange of some performance decrease.
1.20.1 C++接口
#include <vpi/algo/TemporalNoiseReduction.h>

VPIImage input;
vpiImageCreate(width, height, VPI_IMAGE_FORMAT_NV12_ER, 0, &input);

VPIImage prevOutput, output;
vpiImageCreate(width, height, VPI_IMAGE_FORMAT_NV12_ER, 0, &prevOutput);
vpiImageCreate(width, height, VPI_IMAGE_FORMAT_NV12_ER, 0, &output);

VPIPayload tnr;
vpiCreateTemporalNoiseReduction(VPI_BACKEND_CUDA, width, height, VPI_IMAGE_FORMAT_NV12_ER, VPI_TNR_DEFAULT, &tnr);

VPIStream stream;
vpiStreamCreate(0, &stream);
while (FetchFrame(vid, &input))
{
    VPITNRParams params;
    vpiInitTemporalNoiseReductionParams(&params);

    params.preset   = VPI_TNR_PRESET_OUTDOOR_MEDIUM_LIGHT;
    params.strength = 1.0f;

    if (isFirstFrame)
    {
        vpiSubmitTemporalNoiseReduction(stream, VPI_BACKEND_CUDA, tnr, NULL, input, output, &params);
    }
    else
    {
        vpiSubmitTemporalNoiseReduction(stream, VPI_BACKEND_CUDA, tnr, prevOutput, input, output, &params);
    }

    vpiStreamSync(stream);
    VPIImage tmp = output;
    output       = prevOutput;
    prevOutput   = tmp;
}

vpiStreamDestroy(stream);
vpiPayloadDestroy(tnr);
vpiImageDestroy(input);
vpiImageDestroy(prevOutput);
vpiImageDestroy(output);
1.20.2 Python接口
import vpi
with vpi.Backend.CUDA:
    tnr = vpi.TemporalNoiseReduction(size, vpi.Format.NV12_ER)

while inVideo.read(input)[0]:
    denoised = tnr(input, preset=vpi.TNRPreset.INDOOR_MEDIUM_LIGHT, strength=1)
1.21 Pyramidal LK Optical Flow

官方文档是这个。实现基于金字塔的LK光流跟踪。效果如下。 C++需引用vpi/algo/OpticalFlowPyrLK.h,名字为vpiSubmitOpticalFLowPyrLK(),Python需导入vpi包,名字为vpi.OpticalFlowPyrLK()

1.21.1 C++接口
#include <vpi/algo/OpticalFlowPyrLK.h>

VPIStream stream;
vpiStreamCreate(0, &stream);

VPIImage prevImage      = /* previous frame */;
VPIPyramid pyrPrevFrame = /* pyramid out of previous frame */;
VPIPyramid pyrCurFrame  = /* pyramid for current frame */;
VPIArray arrPrevPts     = /* array with previous frame's keypoints, type VPI_ARRAY_TYPE_KEYPOINT_F32 */;
VPIArray arrCurPts      = /* array with current frame's keypoints, type VPI_ARRAY_TYPE_KEYPOINT_F32 */;
VPIArray arrStatus      = /* array with keypoint tracking status, type VPI_ARRAY_TYPE_U8 */;
VPIArray scores         = /* array with keypoint scores, type VPI_ARRAY_TYPE_U8 */;

int levels;
vpiPyramidGetNumLevels(pyrPrevFrame, &levels);

float scale;
vpiPyramidGetScale(pyrPrevFrame, &scale);

VPIImageFormat format;
vpiImageGetFormat(prevImage, &format);

int width, height;
vpiImageGetSize(prevImage, &width, &height);

VPIPayload optflow;
vpiCreateOpticalFlowPyrLK(VPI_BACKEND_CUDA, width, height, format, levels, scale, &optflow);

VPIOpticalFlowPyrLKParams lkParams;
vpiInitOpticalFlowPyrLKParams(&lkParams);

for (int idframe = 1; idframe < frame_count; ++idframe)
{
    curImage = /* "new frame from video sequence */;

    VPI_CHECK_STATUS(
            vpiSubmitGaussianPyramidGenerator(stream, VPI_BACKEND_CUDA, curImage, pyrCurFrame, VPI_BORDER_CLAMP));

    vpiSubmitOpticalFlowPyrLK(stream, VPI_BACKEND_CUDA, optflow, pyrPrevFrame, pyrCurFrame, arrPrevPts, arrCurPts, arrStatus, &lkParams);

    vpiStreamSync(stream);

    VPIImage tmpImg = prevImage;
    prevImage       = curImage;
    curImage        = tmpImg;

    VPIPyramid tmpPyr = pyrPrevFrame;
    pyrPrevFrame      = pyrCurFrame;
    pyrCurFrame       = tmpPyr;

    VPIArray tmpArray = arrPrevPts;
    arrPrevPts        = arrCurPts;
    arrCurPts         = tmpArray;
}

vpiStreamDestroy(stream);
vpiPayloadDestroy(optflow);
vpiPyramidDestroy(pyrPrevFrame);
vpiPyramidDestroy(pyrCurFrame);
vpiArrayDestroy(arrPrevPts);
vpiArrayDestroy(arrCurPts);
vpiArrayDestroy(arrStatus);
1.21.2 Python接口
import vpi

with vpi.Backend.CUDA:
    optflow = vpi.OpticalFlowPyrLK(frame, curFeatures, 4)

while inVideo.read(input)[0]:
    curFeatures, status = optflow(input)
1.22 Dense Optical FLow

官方文档是这个。实现稠密光流跟踪。在VPI中,估计的是4×4 block大小的光流。效果如下。 C++需引用vpi/algo/OpticalFlowDense.h,名字为vpiSubmitOpticalFLowDense(),Python需导入vpi包,名字为vpi.optflow_dense()

1.22.1 C++接口
#include <vpi/algo/OpticalFlowDense.h>

VPIStream stream;
vpiStreamCreate(0, &stream);

VPIImage mvImage;
int32_t mvWidth  = (width + 3) / 4;
int32_t mvHeight = (height + 3) / 4;
vpiImageCreate(mvWidth, mvHeight, VPI_IMAGE_FORMAT_2S16_BL, 0, &mvImage);

VPIPayload optflow;
vpiCreateOpticalFlowDense(VPI_BACKEND_NVENC, width, height, imgFmtBL, quality, &optflow);

VPIImage prevImage = /* previous frame */;

for (int idframe = 1; idframe < frame_count; ++idframe)
{
    VPIImage curImage = /* current frame */;
    vpiSubmitOpticalFlowDense(stream, VPI_BACKEND_NVENC, optflow, prevImage, curImage, mvImage);
    vpiStreamSync(stream);
    VPIImage tmpImg = prevImage;
    prevImage       = curImage;
    curImage        = tmpImg;
}
vpiStreamDestroy(stream);
vpiPayloadDestroy(optflow);
vpiImageDestroy(prevImage);
vpiImageDestroy(curImage);
vpiImageDestroy(mvImage);
1.22.2 Python接口
import vpi

prevImage = inVideo.read()[1]

while inVideo.read(curImage)[0]:
    with vpi.Backend.NVENC:
        motion = vpi.optflow_dense(prevImage, curImage)
    prevImage = curImage
1.23 Image Histogram

官方文档是这个。实现对于影像灰度直方图的统计。需要注意的是,同样一幅影像,CPU版本和CUDA版本得到的结果可能会有稍许的不同,由于计算精度等因素影响。效果如下。 C++需引用vpi/algo/Histogram.h,名字为vpiSubmitHistogram(),Python需导入vpi包,名字为.histogram()

1.23.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <opencv2/imgcodecs.hpp>
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/Histogram.h>   // Histogram头文件
#include <vpi/Array.h>  // VPI Array类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出keypoint
    VPIArray output;
    vpiArrayCreate(256, VPI_ARRAY_TYPE_U32, 0, &output);

    // step6 创建对应Payload
    VPIPayload payload;
    vpiCreateHistogramEven(VPI_BACKEND_CUDA, VPI_IMAGE_FORMAT_U8, 0, 255, 256, &payload);

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 执行操作
    vpiSubmitHistogram(stream, VPI_BACKEND_CUDA, payload, input, output, 0);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIArrayData data;
    vpiArrayLockData(output, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &data);

    // step11 解除对对象的锁定
    vpiArrayUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(payload);
    vpiImageDestroy(input);
    vpiArrayDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.23.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.histogram(256, range=(0, 255))

    # step4 结果输出
    with output.rlock_cpu() as histData:
        print(histData)
1.24 Equalize Histogram

官方文档是这个。用于直方图均衡化,可以调整对比度。效果如下。 C++需引用vpi/algo/EqualizeHist.h,名字为vpiSubmitEqualizeHist(),Python需导入vpi包,名字为.eqhist()

1.24.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/EqualizeHist.h>   // EqualizeHist头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出keypoint
    VPIImage output;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U8, 0, &output);

    // step6 创建对应Payload
    VPIPayload payload;
    vpiCreateEqualizeHist(VPI_BACKEND_CUDA, VPI_IMAGE_FORMAT_U8, &payload);

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 执行操作
    vpiSubmitEqualizeHist(stream, VPI_BACKEND_CUDA, payload, input, output);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step11 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_equal.jpg", out_mat);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step12 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(payload);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.24.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.eqhist()

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_equal.jpg", outData)
1.25 Background Subtractor

官方文档是这个。用于实现背景建模与减除。效果如下。 C++需引用vpi/algo/BackgroundSubtractor.h,名字为vpiSubmitBackgroundSubtractor(),Python需导入vpi包,名字为vpi.BackgroundSubtractor()

1.25.1 C++接口
#include <vpi/algo/BackgroundSubtractor.h>

VPIImage input     = /* frame from the video sequence */;
VPIPyramid fgmask  = /* foreground mask. Format has to be VPI_IMAGE_FORMAT_U8 */;
VPIPyramid bgimage = /* background image. Image format has to be the same as input image format */;

VPIStream stream;
vpiStreamCreate(0, &stream);

VPIPayload payload;
vpiCreateBackgroundSubtractor(VPI_BACKEND_CUDA, w, h, VPI_IMAGE_FORMAT_RGB8, &payload);

for (i = 0; i < nframes; ++i)
{
    input = /* Fetch frame from video sequence */;

    VPIBackgroundSubtractorParams algoParams;
    vpiInitBackgroundSubtractorParams(&algoParams);
    algoParams.learningRate = 0.01;

    vpiSubmitBackgroundSubtractor(stream, VPI_BACKEND_CUDA, payload, input, fgmask, bgimage, &algoParams);

    vpiStreamSync(stream);

    process_results(fgmask, bgimage);
}

vpiStreamDestroy(stream);
vpiPayloadDestroy(payload);
vpiImageDestroy(input);
vpiImageDestroy(fgmask);
vpiImageDestroy(bgimage);
1.25.2 Python接口
import vpi

with vpi.Backend.CUDA:
    bgsub = vpi.BackgroundSubtractor(size, vpi.Format.RGB8)

while inVideo.read(input)[0]:
    fgmask, bgimage = bgsub(input, learnrate=0.01)
1.26 Minimum/Maximum Location

官方文档是这个。用于实现在图像中对最小、最大值的定位。效果如下。 C++需引用vpi/algo/MinMaxLoc.h,名字为vpiSubmitMinMaxLoc(),Python需导入vpi包,名字为.minmaxloc()

1.26.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/MinMaxLoc.h>   // MinMaxLoc头文件
#include <vpi/Array.h>  // VPI Array类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t width, height;
    vpiImageGetSize(input, &width, &height);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出keypoint
    VPIArray minCoords;
    vpiArrayCreate(10000, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &minCoords);

    VPIArray maxCoords;
    vpiArrayCreate(10000, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &maxCoords);

    // step6 创建对应Payload
    VPIPayload payload;
    vpiCreateMinMaxLoc(VPI_BACKEND_CPU, width, height, type, &payload);

    // step7 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step8 执行操作
    vpiSubmitMinMaxLoc(stream, VPI_BACKEND_CPU, payload, input, minCoords, maxCoords);

    // step9 等待所有操作执行完成
    vpiStreamSync(stream);

    // step10 创建锁定对象,取出数据
    VPIArrayData minCoordsData, maxCoordsData;
    vpiArrayLockData(minCoords, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &minCoordsData);
    vpiArrayLockData(maxCoords, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &maxCoordsData);

    VPIKeypointF32 *min_coords = (VPIKeypointF32 *) minCoordsData.buffer.aos.data;
    VPIKeypointF32 *max_coords = (VPIKeypointF32 *) maxCoordsData.buffer.aos.data;

    int min_i = min_coords[0].y;
    int min_j = min_coords[0].x;

    int max_i = max_coords[0].y;
    int max_j = max_coords[0].x;

    vpiArrayUnlock(maxCoords);
    vpiArrayUnlock(minCoords);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step11 销毁相关资源
    vpiStreamDestroy(stream);
    vpiPayloadDestroy(payload);
    vpiImageDestroy(input);
    vpiArrayDestroy(minCoords);
    vpiArrayDestroy(maxCoords);
    // -------------------------------------------------------------------

    return 0;
}
1.26.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CPU:
        min_coords, max_coords = input.minmaxloc(min_capacity=10000, max_capacity=10000)

    # step4 输出结果
    with input.rlock_cpu() as in_data, min_coords.rlock_cpu() as min_data, max_coords.rlock_cpu() as max_data:
        min_loc = tuple(min_data[0].astype(int)[::-1])
        max_loc = tuple(max_data[0].astype(int)[::-1])

        min_value = in_data[min_loc]
        max_value = in_data[max_loc]

        print(min_loc, min_value)
        print(max_loc, max_value)
1.27 Image Flip

官方文档是这个。用于实现影像翻转。效果如下。 C++需引用vpi/algo/ImageFlip.h,名字为vpiSubmitImageFlip(),Python需导入vpi包,名字为.image_flip()。包含如下参数:

  • VPI_FLIP_HORIZ: 水平翻转
  • VPI_FLIP_VERT: 垂直翻转
  • VPI_FLIP_BOTH: 水平、垂直同时翻转
1.27.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/ImageFlip.h>   // ImageFlip头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出keypoint
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitImageFlip(stream, VPI_BACKEND_CPU, input, output, VPI_FLIP_BOTH);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_flip.jpg", out_mat);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step11 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.27.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CPU:
        output = input.image_flip(vpi.Flip.BOTH)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_flip.jpg", outData)
1.28 Median Filter

官方文档是这个。用于实现中值滤波。效果如下。 C++需引用vpi/algo/MedianFilter.h,名字为vpiSubmitMedianFilter(),Python需导入vpi包,名字为.median_filter()

1.28.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/MedianFilter.h>   // MedianFilter头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIImage output;
    vpiImageCreate(w, h, type, 0, &output);

    int8_t kernel[3 * 3] = {1, 1, 1,
                            1, 1, 1,
                            1, 1, 1};

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitMedianFilter(stream, VPI_BACKEND_CPU, input, output, 3, 3, kernel, VPI_BORDER_LIMITED);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_median.jpg", out_mat);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step11 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.28.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    kernel = [[1, 1, 1],
              [1, 1, 1],
              [1, 1, 1]]

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.median_filter(kernel, border=vpi.Border.ZERO)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_median.jpg", outData)
1.29 FAST Corner Detector

官方文档是这个。用于实现FAST角点检测。关于FAST角点的原理和实现细节见上面的这个文档。效果如下。 C++需引用vpi/algo/FASTCorners.h,名字为vpiSubmitFASTCornerDetector(),Python需导入vpi包,名字为.fastcorners()

1.29.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/FASTCorners.h>   // FASTCorners头文件
#include <vpi/Array.h>  // VPI Array类头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIArray corners;
    vpiArrayCreate(1000, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &corners);

    VPIFASTCornerDetectorParams params;
    vpiInitFASTCornerDetectorParams(&params);
    params.circleRadius = 3;
    params.arcLength = 9;
    params.intensityThreshold = 142;
    params.nonMaxSuppression = 1;

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitFASTCornerDetector(stream, VPI_BACKEND_CPU, input, corners, &params, VPI_BORDER_LIMITED);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIArrayData outData;
    vpiArrayLockData(corners, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outData);

    VPIKeypointF32 *kps = (VPIKeypointF32 *) outData.buffer.aos.data;

    int pos_i = kps[0].y;
    int pos_j = kps[0].x;

    cout << pos_i << " " << pos_j << endl;

    vpiArrayUnlock(corners);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step10 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiArrayDestroy(corners);
    // -------------------------------------------------------------------

    return 0;
}
1.29.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CPU:
        corners = input.fastcorners()

    # step4 结果输出
    with corners.rlock_cpu() as corners_data:
        corners_loc = tuple(corners_data[0].astype(int)[::-1])
        print(corners_loc)
1.30 Mix Channels

官方文档是这个。用于实现多通道影像的相关通道操作,比如拆分通道、合并通道,重新调整顺序等。效果如下。 C++需引用vpi/algo/MixChannels.h,名字为vpiSubmitMixChannels(),Python需导入vpi包,名字为vpi.mixchannels()

VPI中的通道重映射是通过两个Array实现的(一个是通道在原始影像中的索引,另一个是通道在新影像中的索引),原理示意图如下所示。 图中,蓝色框框表示通道,合在一起表示一个图像。蓝色框框中的数字表示该通道在对应影像中的通道索引。蓝色框框外面的数字表示通道映射Array中使用的索引。VPI会根据这个索引来映射通道。比如,对于Split Channels,原始影像中的通道索引分别是0、1、2,我们要拆分成三个影像,所以,对于新的第一个影像,对应的就是0,第二个是1,第三个是2。他们都是单通道影像,所以在他们内部,通道的索引只会为0。而Merge Channels则相反。事实上,这个图可能没有那么好理解,我自己画了个图,可能更方便理解。 如果用一句话概括规律那就是,所有输入影像的通道索引构成Input Array,所有输出影像的通道索引构成Output Array。这里的索引是可以跨图像编号的。比如我们分离通道,分别建了3个RGB影像,那么一共就是有9个通道,索引从0到8。我们要找的就是输入影像三个通道在所有输出影像中所对应的通道。而如果我们创建的是三个单通道影像,那么索引就还是0、1、2了。

1.30.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/MixChannels.h>   // MixChannels头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_COLOR);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIImage outputs[3];
    for (int i = 0; i < 3; ++i) {
        vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U8, 0, &outputs[i]);
    }

    int mappingIn[3] = {0, 1, 2};
    int mappingOut[3] = {0, 1, 2};

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitMixChannels(stream, VPI_BACKEND_CPU, &input, 1, outputs, 3, mappingIn, mappingOut, 3);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    for (int i = 0; i < 3; ++i) {
        VPIImageData outData;
        vpiImageLockData(outputs[i], VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

        // step13 将取出的数据转换为OpenCV格式并保存
        Mat out_mat;
        vpiImageDataExportOpenCVMat(outData, &out_mat);
        imwrite("../test-data/out_band_" + to_string(i + 1) + ".jpg", out_mat);

        vpiImageUnlock(outputs[i]);
    }
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step10 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    for (int i = 0; i < 3; ++i) {
        vpiImageDestroy(outputs[i]);
    }
    // -------------------------------------------------------------------

    return 0;
}
1.30.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_COLOR)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    outputs = [vpi.Image(input.size, vpi.Format.U8),
               vpi.Image(input.size, vpi.Format.U8),
               vpi.Image(input.size, vpi.Format.U8)]

    # step3 执行操作
    with vpi.Backend.CPU:
        vpi.mixchannels([input], outputs, [0, 1, 2], [0, 1, 2])

    # step4 结果输出
    for i in range(len(outputs)):
        output = outputs[i]
        with output.rlock_cpu() as outData:
            cv2.imwrite("../test-data/out_band_" + str(i + 1) + ".jpg", outData)
1.31 Canny Edge Detector

官方文档是这个。用于实现经典Canny边缘检测算子。效果如下。 C++需引用vpi/algo/CannyEdges.h,名字为vpiSubmitCannyEdgeDetector(),Python需导入vpi包,名字为.canny()

1.31.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/CannyEdges.h>   // MixChannels头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIImage output;
    vpiImageCreate(w, h, VPI_IMAGE_FORMAT_U8, 0, &output);

    VPIPayload payload;
    vpiCreateCannyEdgeDetector(VPI_BACKEND_CUDA, w, h, &payload);

    VPICannyEdgeDetectorParams params;
    vpiInitCannyEdgeDetectorParams(&params);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    float thresholdStrong = 300;
    float thresholdWeak = 100;
    float edgeValue = 255;
    float nonEdgeValue = 0;
    vpiSubmitCannyEdgeDetector(stream, VPI_BACKEND_CUDA, payload, input, output, thresholdStrong,
                               thresholdWeak, edgeValue, nonEdgeValue, &params);

    // step8 等待所有操作执行完成
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step10 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    imwrite("../test-data/out_canny.jpg", out_mat);

    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step11 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.31.2 Python接口
import cv2
import vpi  # 导入VPI包

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
with vpi.Backend.CUDA:
    output = input.canny(thresh_strong=300, thresh_weak=100, edge_value=255, nonedge_value=0, norm=vpi.Norm.L2)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        cv2.imwrite("../test-data/out_canny.jpg", outData)
1.32 ORB Feature Detector

官方文档是这个。用于实现ORB特征提取。效果如下。 C++需引用vpi/algo/ORB.h,名字为vpiSubmitORBFeatureDetector()。需要注意的是,VPI的ORB函数,目前没有Python版本的实现。

1.32.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/algo/ORB.h>   // ORB头文件
#include <vpi/Pyramid.h>    // Pyramid头文件
#include <vpi/Array.h>  // Array头文件
#include <vpi/algo/GaussianPyramid.h>

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIArray corners;
    vpiArrayCreate(1000, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &corners);

    VPIArray descriptors;
    vpiArrayCreate(1000, VPI_ARRAY_TYPE_BRIEF_DESCRIPTOR, 0, &descriptors);

    VPIPayload payload;
    vpiCreateORBFeatureDetector(VPI_BACKEND_CPU, 10000, &payload);

    VPIORBParams params;
    vpiInitORBParams(&params);

    VPIPyramid inputPyr;
    vpiPyramidCreate(w, h, VPI_IMAGE_FORMAT_U8, params.pyramidLevels, 0.5, VPI_BACKEND_CPU, &inputPyr);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 创建高斯金字塔
    vpiSubmitGaussianPyramidGenerator(stream, VPI_BACKEND_CPU, input, inputPyr, VPI_BORDER_ZERO);
    vpiStreamSync(stream);

    // step8 提取ORB特征
    vpiSubmitORBFeatureDetector(stream, VPI_BACKEND_CPU, payload, inputPyr, corners, descriptors, &params,
                                VPI_BORDER_ZERO);
    vpiStreamSync(stream);

    // step9 创建锁定对象,取出数据
    VPIArrayData outData;
    vpiArrayLockData(corners, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outData);

    VPIKeypointF32 *kps = (VPIKeypointF32 *) outData.buffer.aos.data;

    int pos_i = kps[0].y;
    int pos_j = kps[0].x;

    cout << pos_i << " " << pos_j << endl;

    vpiArrayUnlock(corners);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step10 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiArrayDestroy(corners);
    vpiArrayDestroy(descriptors);
    // -------------------------------------------------------------------

    return 0;
}
1.33 Image Statistics

官方文档是这个。用于实现对于影像一些统计信息的获取,包括:每个通道的像素个数(非零像素)、灰度均值、灰度总和、灰度方差、影像通道间的协方差。VPI最多支持4个通道影像的信息统计。效果如下。 C++需引用vpi/algo/ImageStats.h,名字为vpiSubmitImageStats(),Python需导入vpi包,名字为.image_stats()。函数的参数含义如下:

  • VPI_STAT_PIXEL_COUNT: 计算影像中非零像素的个数
  • VPI_STAT_SUM:计算每个通道的非零灰度值之和,输出float类型
  • VPI_STAT_MEAN:计算每个通道的非零灰度值的均值,输出float类型
  • VPI_STAT_VARIANCE:计算每个通道的非零灰度值的方差,输出float类型
  • VPI_STAT_COVARIANCE:计算通道之间的协方差矩阵,元素类型为float
1.33.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/Array.h>  // Array头文件
#include <vpi/algo/ImageStats.h>

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img1.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat img1 = imread(img_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input;
    vpiImageCreateWrapperOpenCVMat(img1, 0, &input);

    // step3 获取影像大小
    int32_t w, h;
    vpiImageGetSize(input, &w, &h);

    // step4 获取影像格式
    VPIImageFormat type;
    vpiImageGetFormat(input, &type);

    // step5 创建输出
    VPIArray output;
    vpiArrayCreate(1, VPI_ARRAY_TYPE_STATISTICS, 0, &output);

    // step6 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitImageStats(stream, VPI_BACKEND_CUDA, input, output, NULL, VPI_STAT_COVARIANCE);
    vpiStreamSync(stream);

    // step8 创建锁定对象,取出数据
    VPIArrayData outData;
    vpiArrayLockData(output, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outData);

    vpiArrayUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step9 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiArrayDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.33.2 Python接口
import cv2
import vpi  # 导入VPI包
import numpy as np

if __name__ == '__main__':
    img_path = "../test-data/img1.png"

    # step1 利用OpenCV读取影像
    img1 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(img1)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = input.image_stats(flags=vpi.ImageStatistics.COVARIANCE)

    # step4 结果输出
    stats = output.cpu().view(np.recarray)[0]
    print(stats.covariance[0][0])
1.34 Template Matching

官方文档是这个。用于实现模板匹配。在VPI中,使用归一化互相关系数(NCC)作为相似性指标。效果如下。 C++需引用vpi/algo/TemplateMatching.h,名字为vpiSubmitTemplateMatching(),Python需导入vpi包,名字为vpi.templateMatching()

1.34.1 C++接口
#include <iostream> // C++标准头文件
#include <opencv2/opencv.hpp>   // OpenCV头文件
#include <vpi/OpenCVInterop.hpp>    // VPI与OpenCV互操作的头文件
#include <vpi/Image.h>  // VPI Image类头文件
#include <vpi/Stream.h> // VPI Stream类头文件
#include <vpi/Array.h>  // Array头文件
#include <vpi/algo/TemplateMatching.h>  // TemplateMatching头文件

using namespace cv;
using namespace std;

int main() {
    string img_path = "../test-data/img2.png";
    string templ_path = "../test-data/img2-template.png";

    // 第一阶段:初始化
    // -------------------------------------------------------------------
    // step1 利用OpenCV读取影像
    Mat src_img = imread(img_path, IMREAD_GRAYSCALE);
    Mat templ_img = imread(templ_path, IMREAD_GRAYSCALE);

    // step2 基于读取的影像构造VPIImage对象
    VPIImage input, templ;
    vpiImageCreateWrapperOpenCVMat(src_img, 0, &input);
    vpiImageCreateWrapperOpenCVMat(templ_img, 0, &templ);

    // step3 获取影像大小
    int32_t srcW, srcH;
    vpiImageGetSize(input, &srcW, &srcH);

    int32_t templW, templH;
    vpiImageGetSize(templ, &templW, &templH);

    // step4 创建输出
    VPIImage output;
    vpiImageCreate(srcW - templW + 1, srcH - templH + 1, VPI_IMAGE_FORMAT_F32, 0, &output);

    // step5 创建执行流
    VPIStream stream;
    vpiStreamCreate(0, &stream);

    // step6 创建对应payload
    VPIPayload payload;
    vpiCreateTemplateMatching(VPI_BACKEND_CUDA, srcW, srcH, &payload);

    vpiTemplateMatchingSetSourceImage(stream, VPI_BACKEND_CUDA, payload, input);

    vpiTemplateMatchingSetTemplateImage(stream, VPI_BACKEND_CUDA, payload, templ, NULL);
    // -------------------------------------------------------------------

    // 第二阶段:处理并保存结果
    // -------------------------------------------------------------------
    // step7 执行操作
    vpiSubmitTemplateMatching(stream, VPI_BACKEND_CUDA, payload, output, VPI_TEMPLATE_MATCHING_NCC);
    vpiStreamSync(stream);

    // step8 创建锁定对象,取出数据
    VPIImageData outData;
    vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData);

    // step9 将取出的数据转换为OpenCV格式并保存
    Mat out_mat;
    vpiImageDataExportOpenCVMat(outData, &out_mat);
    out_mat = out_mat * 255;
    imwrite("../test-data/out_templ.jpg", out_mat);

    vpiImageUnlock(output);
    // -------------------------------------------------------------------

    // 第三阶段:清理资源
    // -------------------------------------------------------------------
    // step10 销毁相关资源
    vpiStreamDestroy(stream);
    vpiImageDestroy(input);
    vpiImageDestroy(templ);
    vpiImageDestroy(output);
    // -------------------------------------------------------------------

    return 0;
}
1.34.2 Python接口
import cv2
import vpi  # 导入VPI包
import numpy as np

if __name__ == '__main__':
    img_path = "../test-data/img2.png"
    templ_path = "../test-data/img2-template.png"

    # step1 利用OpenCV读取影像
    src_img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
    templ_img = cv2.imread(templ_path, cv2.IMREAD_GRAYSCALE)

    # step2 将读取的Numpy Array对象转换成VPI的影像格式
    input = vpi.asimage(src_img)
    templ = vpi.asimage(templ_img)

    # step3 执行操作
    with vpi.Backend.CUDA:
        output = vpi.templateMatching(input, templ)

    # step4 结果输出
    with output.rlock_cpu() as outData:
        outData = outData * 255
        cv2.imwrite("../test-data/out_templ.jpg", outData)

2.汇总表格

为了更方便查阅,我们把上面的内容进行简单概率,总结了一个表格,展示了头文件、函数、作用的对应关系。

函数名(Python) 包名 函数名(C++) 头文件 简介
.box_filter() vpi vpiSubmitBoxFilter() vpi/algo/BoxFilter.h 低通滤波器,实现对图像的平滑(局部窗口取平均赋值)
.bilateral_filter() vpi vpiSubmitBilateralFilter() vpi/algo/BilateralFilter.h 经典双边滤波器,实现图像平滑
.gaussian_filter() vpi vpiSubmitGaussianFilter() vpi/algo/GaussionFilter.h 经典高斯滤波器,实现图像平滑
.gaussian_pyramid() vpi vpiSubmitGaussianPyramidGenerator() vpi/algo/GaussianPyramid.h 创建高斯金字塔
.laplacian_pyramid() vpi vpiSubmitLaplacianPyramidGenerator() vpi/algo/LaplacianPyramid.h 创建拉普拉斯金字塔
.erode() vpi vpiSubmitErode() vpi/algo/MorphologicalFilter.h 腐蚀操作
.dilate() vpi vpiSubmitDilate() vpi/algo/MorphologicalFilter.h 膨胀操作
.convolution() vpi vpiSubmitConvolution() vpi/algo/Convolution.h 卷积操作,支持自定义卷积核
.convolution() vpi vpiSubmitSeparableConvolution() vpi/algo/Convolution.h 可分离卷积,效率更高
.convert() vpi vpiSubmitConvertImageFormat() vpi/algo/ConvertImageFormat.h 转换影像数据格式
.rescale() vpi vpiSubmitRescale() vpi/algo/Rescale.h 影像缩放
.remap() vpi vpiSubmitRemap() vpi/algo/Remap.h 基于给定关系实现影像的变换
.perspwarp() vpi vpiSubmitPerspectiveWarp() vpi/algo/PerspectiveWarp.h 实现影像投影变换
.fft() vpi vpiSubmitFFT() vpi/algo/FFT.h 实现傅里叶变换
.irfft() vpi vpiSubmitIFFT() vpi/algo/FFT.h 实现逆傅里叶变换
.fisheye_correction() vpi vpiWarpMapGenerateFromFisheyeLensDistortionModel() vpi/algo/LensDistortionModels.h 实现镜头畸变校正
vpi.stereodisp() vpi vpiCreateStereoDisparityEstimator() vpi/algo/StereoDisparity.h 实现双目视差估计
vpi.KLTFeatureTracker() vpi vpiSubmitKLTFeatureTracker() vpi/algo/KLTFeatureTracker.h 实现KLT跟踪
vpi.harriscorners() vpi vpiSubmitHarrisCornerDetector() vpi/algo/HarrisCorners.h 提取Harris角点
vpi.TemporalNoiseReduction() vpi vpiSubmitTemporalNoiseReduction() vpi/algo/TemporalNoiseReduction.h 实现时空噪声去除
vpi.OpticalFlowPyrLK() vpi vpiSubmitOpticalFLowPyrLK() vpi/algo/OpticalFlowPyrLK.h 实现LK光流跟踪
vpi.optflow_dense() vpi vpiSubmitOpticalFLowDense() vpi/algo/OpticalFlowDense.h 实现稠密光流跟踪
.histogram() vpi vpiSubmitHistogram() vpi/algo/Histogram.h 生成影像的灰度直方图
.eqhist() vpi vpiSubmitEqualizeHist() vpi/algo/EqualizeHist.h 直方图均衡化
vpi.BackgroundSubtractor() vpi vpiSubmitBackgroundSubtractor() vpi/algo/BackgroundSubtractor.h 实现背景减除
.minmaxloc() vpi vpiSubmitMinMaxLoc() vpi/algo/MinMaxLoc.h 返回图像中最大最小值的位置
.image_flip() vpi vpiSubmitImageFlip() vpi/algo/ImageFlip.h 实现影像翻转
.median_filter() vpi vpiSubmitMedianFilter() vpi/algo/MedianFilter.h 中值滤波
.fastcorners() vpi vpiSubmitFASTCornerDetector() vpi/algo/FASTCorners.h FAST角点提取
vpi.mixchannels() vpi vpiSubmitMixChannels() vpi/algo/MixChannels.h 影像通道合并、拆分等操作
.canny() vpi vpiSubmitCannyEdgeDetector() vpi/algo/CannyEdges.h Canny边缘检测算子
- - vpiSubmitORBFeatureDetector() vpi/algo/ORB.h ORB特征提取
.image_stats() vpi vpiSubmitImageStats() vpi/algo/ImageStats.h 影像信息统计,包括灰度均值、总和、方差等
vpi.templateMatching() vpi vpiSubmitTemplateMatching() vpi/algo/TemplateMatching.h 模板匹配

上表为目前VPI中各函数的简要说明。

3.参考资料

  • [1] https://docs.nvidia.com/vpi/algorithms.html
  • [2] https://docs.nvidia.com/vpi/algo_box_filter.html
  • [3] https://docs.nvidia.com/vpi/algo_bilat_filter.html
  • [4] https://docs.nvidia.com/vpi/algo_gaussian_filter.html
  • [5] https://docs.nvidia.com/vpi/algo_gaussian_pyramid_generator.html
  • [6] https://docs.nvidia.com/vpi/algo_laplacian_pyramid_generator.html
  • [7] https://docs.nvidia.com/vpi/algo_erode.html
  • [8] https://docs.nvidia.com/vpi/algo_dilate.html
  • [9] https://docs.nvidia.com/vpi/algo_convolution.html
  • [10] https://docs.nvidia.com/vpi/algo_sep_convolution.html
  • [11] https://docs.nvidia.com/vpi/algo_imageconv.html
  • [12] https://docs.nvidia.com/vpi/algo_rescale.html
  • [13] https://docs.nvidia.com/vpi/algo_remap.html
  • [14] https://docs.nvidia.com/vpi/algo_persp_warp.html
  • [15] https://docs.nvidia.com/vpi/algo_fft.html
  • [16] https://docs.nvidia.com/vpi/algo_ifft.html
  • [17] https://docs.nvidia.com/vpi/algo_ldc.html
  • [18] https://docs.nvidia.com/vpi/algo_stereo_disparity.html
  • [19] https://docs.nvidia.com/vpi/algo_klt_tracker.html
  • [20] https://docs.nvidia.com/vpi/algo_harris_corners.html
  • [21] https://docs.nvidia.com/vpi/algo_tnr.html
  • [22] https://docs.nvidia.com/vpi/algo_optflow_lk.html
  • [23] https://docs.nvidia.com/vpi/algo_optflow_dense.html
  • [24] https://docs.nvidia.com/vpi/algo_histogram.html
  • [25] https://docs.nvidia.com/vpi/algo_equalize_hist.html
  • [26] https://docs.nvidia.com/vpi/algo_background_subtractor.html
  • [27] https://docs.nvidia.com/vpi/algo_minmaxloc.html
  • [28] https://docs.nvidia.com/vpi/algo_image_flip.html
  • [29] https://docs.nvidia.com/vpi/algo_median_filter.html
  • [30] https://docs.nvidia.com/vpi/algo_fast_corners_detector.html
  • [31] https://docs.nvidia.com/vpi/algo_mix_channels.html
  • [32] https://docs.nvidia.com/vpi/algo_canny_edge_detector.html
  • [33] https://docs.nvidia.com/vpi/algo_orb_feature_detector.html
  • [34] https://docs.nvidia.com/vpi/algo_image_stats.html
  • [35] https://docs.nvidia.com/vpi/algo_template_matching.html

本文作者原创,未经许可不得转载,谢谢配合

返回顶部