nppi 调整大小功能,3 个通道得到奇怪的输出

Bri*_*Yeh 2 c++ opencv cuda nvidia

使用 nppi cuda 库中的 nppi 几何变换函数时,出现奇怪的错误。代码在这里:

#include <nppi.h>
#include <nppi_geometry_transforms.h>

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <vector>

void write(const cv::Mat &mat1, const std::string &path) {
    auto mat2 = cv::Mat(mat1.rows, mat1.cols, CV_8UC4);
    for (int i = 0; i < mat1.rows; i++) {
        for (int j = 0; j < mat1.cols; j++) {
            auto &bgra = mat2.at<cv::Vec4b>(i, j);
            auto &rgb = mat1.at<cv::Vec3b>(i, j);
            bgra[0] = rgb[2];
            bgra[1] = rgb[1];
            bgra[2] = rgb[0];
            bgra[3] = UCHAR_MAX;
        }
    }
    std::vector<int> compression_params;
    compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION);
    compression_params.push_back(9);
    cv::imwrite(path, mat2, compression_params);
}

int main() {
    std::cout << "Hello, World!" << std::endl;
    auto mat = cv::Mat(256, 256, CV_8UC3);
    for (int i = 0; i < mat.rows; i++) {
        for (int j = 0; j < mat.cols; j++) {
            auto &rgb = mat.at<cv::Vec3b>(i, j);
            rgb[0] = (uint8_t)j;
            rgb[1] = (uint8_t)i;
            rgb[2] = (uint8_t)(UCHAR_MAX - j);
        }
    }
    write(mat, "./test.png");
    uint8_t *gpuBuffer1;
    uint8_t *gpuBuffer2;
    cudaMalloc(&gpuBuffer1, mat.total());
    cudaMalloc(&gpuBuffer2, mat.total());
    cudaMemcpy(gpuBuffer1, mat.data, mat.total(), cudaMemcpyHostToDevice);
    auto status = nppiResize_8u_C3R(
        gpuBuffer1, mat.cols * 3, {.width = mat.cols, .height = mat.rows},
        {.x = 0, .y = 0, .width = mat.cols, .height = mat.rows}, gpuBuffer2,
        mat.cols * 3, {.width = mat.cols, .height = mat.rows},
        {.x = 0, .y = 0, .width = mat.cols, .height = mat.rows},
        NPPI_INTER_NN);
    if (status != NPP_SUCCESS) {
        std::cerr << "Error executing Resize -- code: " << status << std::endl;
    }
    auto mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3);
    cudaMemcpy(mat2.data, gpuBuffer2, mat.total(), cudaMemcpyDeviceToHost);
    write(mat2, "./test1.png");
}
Run Code Online (Sandbox Code Playgroud)

基本上我展示了一张彩虹图片。然后将其写入 GPU,然后将其大小调整为完全相同的大小,然后将其复制回主机,然后再次显示。我得到的是返回图片的大约 2/3 秒内的乱码数据。

原来的 调整大小

第一张图片是输入图片。第二个输入图片是输出图片。

我希望两张照片是一样的。

如果我通过偏移调整 ROI 并更改目标缓冲区的宽度和高度,则调整大小的图片顶部 1/3 中的像素实际上会正确移动并调整大小。但图片的其余部分是乱码。不知道出了什么问题。有 cuda nppi 库或图像处理经验的人是否知道发生了什么?

下面包含 CMake 文件,以方便任何想要编译它的人。您必须将 opencv 和 cuda 工具包安装为 C++ 库:

cmake_minimum_required(VERSION 3.18)
project(test_nppi)
enable_language(CUDA)
set(CMAKE_CXX_STANDARD 17)

find_package(CUDAToolkit REQUIRED)
find_package(OpenCV)

message(STATUS ${CUDAToolkit_INCLUDE_DIRS})
add_executable(test_nppi main.cu)
target_link_libraries(test_nppi ${OpenCV_LIBS} CUDA::nppig)
target_include_directories(test_nppi PUBLIC ${OpenCV_INCLUDE_DIRS} ${CUDAToolkit_INCLUDE_DIRS})

set_target_properties(test_nppi PROPERTIES
        CUDA_SEPARABLE_COMPILATION ON)
Run Code Online (Sandbox Code Playgroud)

我之前用过nppi调整大小功能来处理单通道图片,但没有这个问题。3 通道 nppi 调整大小函数的输出很奇怪,我想我没有完全理解输入参数。由于有 3 个颜色通道,步长乘以 3,但所有其他尺寸只是以像素为单位测量尺寸;src 和destination 的大小是相同的...不知道我在这里不明白什么。

Rot*_*tem 6

问题是mat.total()等于像素总数,而不是字节总数。

根据OpenCV 文档

总计 ()常量
返回数组元素的总数。

在您的代码示例中,mat.total()等于256*256,而总字节数等于 256*256*3(RGB 每个像素应用 3 个字节)。
(在 OpenCV 术语中“数组元素”相当于图像像素)。

cudaMemcpy(gpuBuffer1, mat.data, mat.total()...仅复制图像总字节的 1/3,因此只有图像数据的上 1/3 有效。


根据这篇文章,计算字节数的正确方法是:

size_t mat_size_in_bytes = mat.step[0] * mat.rows;
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,对于CV_8UC3, mat.step[0]= mat.cols*3,但为了覆盖所有情况,我们最好使用mat.step[0]


更正后的代码示例:

#include "nppi.h"
#include "nppi_geometry_transforms.h"

#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include <vector>

void write(const cv::Mat& mat1, const std::string& path) {
    auto mat2 = cv::Mat(mat1.rows, mat1.cols, CV_8UC4);
    for (int i = 0; i < mat1.rows; i++) {
        for (int j = 0; j < mat1.cols; j++) {
            auto& bgra = mat2.at<cv::Vec4b>(i, j);
            auto& rgb = mat1.at<cv::Vec3b>(i, j);
            bgra[0] = rgb[2];
            bgra[1] = rgb[1];
            bgra[2] = rgb[0];
            bgra[3] = UCHAR_MAX;
        }
    }
    std::vector<int> compression_params;
    compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION);
    compression_params.push_back(9);
    cv::imwrite(path, mat2, compression_params);
}

int main() {
    std::cout << "Hello, World!" << std::endl;
    auto mat = cv::Mat(256, 256, CV_8UC3);
    auto mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3);
    for (int i = 0; i < mat.rows; i++) {
        for (int j = 0; j < mat.cols; j++) {
            auto& rgb = mat.at<cv::Vec3b>(i, j);
            rgb[0] = (uint8_t)j;
            rgb[1] = (uint8_t)i;
            rgb[2] = (uint8_t)(UCHAR_MAX - j);
        }
    }
    write(mat, "./test.png");
    uint8_t* gpuBuffer1;
    uint8_t* gpuBuffer2;
    size_t mat_size_in_bytes = mat.step[0] * mat.rows;  // /sf/ask/1850875071/
    size_t mat2_size_in_bytes = mat2.step[0] * mat2.rows;
    cudaMalloc(&gpuBuffer1, mat_size_in_bytes);
    cudaMalloc(&gpuBuffer2, mat2_size_in_bytes);
    cudaMemcpy(gpuBuffer1, mat.data, mat_size_in_bytes, cudaMemcpyHostToDevice);

    NppiSize oSrcSize = { mat.cols, mat.rows };
    NppiRect oSrcRectROI = { 0, 0, mat.cols, mat.rows };
    NppiSize oDstSize = { mat2.cols, mat2.rows };
    NppiRect oDstRectROI = { 0, 0, mat2.cols, mat2.rows };

    auto status = nppiResize_8u_C3R(
        gpuBuffer1, mat.step[0], oSrcSize,
        oSrcRectROI, gpuBuffer2,
        mat2.step[0], oDstSize,
        oDstRectROI,
        NPPI_INTER_NN);

    if (status != NPP_SUCCESS) {
        std::cerr << "Error executing Resize -- code: " << status << std::endl;
    }
    
    cudaMemcpy(mat2.data, gpuBuffer2, mat2_size_in_bytes, cudaMemcpyDeviceToHost);
    write(mat2, "./test1.png");
}
Run Code Online (Sandbox Code Playgroud)

输出:
在此输入图像描述