如何正确使用 nppiYUV422ToRGB_8u_C2C3R()?

How can I properly use nppiYUV422ToRGB_8u_C2C3R()?

我正在尝试使用 CPU 和 GPU 将 YUV422 图像转换为 RGB 图像。

[在 CPU] 我尝试使用 cv::cvtColor() 如下所示:

cv::cvtColor(mat_UYVY, mat_bgr, cv::COLOR_YUV2BGR_UYVY);

而且效果很好。请参阅此处所附的图片。

[但在 GPU 上] 我尝试使用 nppiYUV422ToRGB_8u_C2C3R(),如下所示:

    NppStatus status = nppiYUV422ToRGB_8u_C2C3R(gpu_buff_UYVY,
                                                img_size.width*2,
                                                gpu_buff_RGB,
                                                img_size.width*3,
                                                roi);

但它会产生一些奇怪的图像,如此处所附。

我知道 opencv 使用 BGR 图像,nppiYUV422ToRGB_8u_C2C3R() 生成的图像是 RGB 图像。正确的。但问题似乎不止于此。我尝试将 RGB 转换为 BGR,但问题仍然存在。不止于此...

有人可以给我一些建议吗?我希望对我有一些建议。谢谢!

#include <iterator>
#include <fstream>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "nppdefs.h"
#include "nppi_support_functions.h"
#include "nppi_color_conversion.h"



int main()
{
  cv::Size img_size(1920, 1080);
  unsigned char *buff_UYVY =
    new unsigned char[img_size.width * img_size.height * 2];
  unsigned char *buff_RGB =
    new unsigned char[img_size.width * img_size.height * 3];

  //file >>> buff_UYVY
  {
    std::string   file_uyvy("uyvy422.raw");
    std::ifstream stream_uyvy;
    stream_uyvy.open(file_uyvy, std::ios::in | std::ios::binary);

    if (!stream_uyvy.is_open())
    {
      std::cerr << "[ERROR] cannot open the raw file " << file_uyvy
        << std::endl;
      std::cerr << std::endl;
      assert(0);
    }
    stream_uyvy.read((char*)buff_UYVY, img_size.width*img_size.height*2);
    stream_uyvy.close();
  }


  //buff_UYVY >>> mat_bgr
  cv::Mat mat_UYVY(img_size, CV_8UC2, buff_UYVY);
  cv::Mat mat_bgr(img_size, CV_8UC3);
  cv::cvtColor(mat_UYVY, mat_bgr, cv::COLOR_YUV2BGR_UYVY);
  cv::imshow("BGR Image from CPU", mat_bgr);
  cv::imwrite("mat_bgr.bmp", mat_bgr);
  cv::imwrite("mat_bgr.jpg", mat_bgr);


  //buff_UYVY >>> buff_RGB
  {
    Npp8u* gpu_buff_UYVY;
    Npp8u* gpu_buff_RGB;
    cudaError_t err_cu_api;
    err_cu_api = cudaMalloc((void**)&gpu_buff_UYVY,
                            img_size.width*img_size.height*2);
    std::cout << "cudaMalloc1 : " << err_cu_api << std::endl;
    err_cu_api = cudaMemcpy((void*)gpu_buff_UYVY,
                            (const void*)buff_UYVY,
                            img_size.width*img_size.height*2,
                            cudaMemcpyHostToDevice);
    std::cout << "cudaMemcpy2 : " << err_cu_api << std::endl;


    err_cu_api = cudaMalloc((void**)&gpu_buff_RGB,
                            img_size.width*img_size.height*3);
    std::cout << "cudaMalloc3 : " << err_cu_api << std::endl;

    NppiSize roi = {img_size.width, img_size.height};
    NppStatus status = nppiYUV422ToRGB_8u_C2C3R(gpu_buff_UYVY,
                                                img_size.width*2,
                                                gpu_buff_RGB,
                                                img_size.width*3,
                                                roi);
    std::cout << "NppStatus : " << status << std::endl;

    err_cu_api = cudaMemcpy((void*) buff_RGB,
                            (const void*)gpu_buff_RGB,
                            img_size.width*img_size.height*3,
                            cudaMemcpyDeviceToHost);
    std::cout << "cudaMemcpy4 : " << err_cu_api << std::endl;
    cudaFree(gpu_buff_UYVY);
    cudaFree(gpu_buff_RGB);
  }

  cv::Mat mat_rgb(img_size, CV_8UC3, buff_RGB);
//cv::cvtColor(mat_rgb, mat_rgb, cv::COLOR_RGB2BGR);

  std::cout << "depth : " << mat_rgb.depth() << std::endl;
  std::cout << "channels : " << mat_rgb.channels() << std::endl;
  std::cout << "elemSize : " << mat_rgb.elemSize() << std::endl;
  std::cout << "step1 : " << mat_rgb.step1() << std::endl;
  std::cout << "type : " << mat_rgb.type() << std::endl;

  try {
    cv::imshow("RGB Image from GPU", mat_rgb);
    cv::imwrite("mat_rgb.bmp", mat_rgb);
    cv::imwrite("mat_rgb.jpg", mat_rgb);
  } catch( cv::Exception& e ) {
    const char* err_msg = e.what();
    std::cout << "exception caught #2: " << err_msg << std::endl;
  }

//  cv::waitKey(0);

  delete[] buff_UYVY;
  delete[] buff_RGB;

  return 0;
}

输出信息如下图:

cudaMalloc1 : 0
cudaMemcpy2 : 0
cudaMalloc3 : 0
NppStatus : 0
cudaMemcpy4 : 0
depth : 0
channels : 3
elemSize : 3
step1 : 5760
type : 16

我认为你的主要问题是openCV UYVY格式与NPP YUV422格式的存储顺序不匹配

OpenCV UYVY存储格式为:U0 Y0 V0 Y1

NPP 格式 is: Y0 U0 Y1 V0

我无法在 Internet 上找到任何“原始”UYVY 编码图像文件,您也没有提供。所以我选择使用合成图像。这是一个测试用例:

$ cat t30.cu
#include <iterator>
#include <fstream>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "nppdefs.h"
#include "nppi_support_functions.h"
#include "nppi_color_conversion.h"



int main()
{
  cv::Size img_size(1920, 1080);
  unsigned char *buff_UYVY =
    new unsigned char[img_size.width * img_size.height * 2];
  unsigned char *buff_RGB =
    new unsigned char[img_size.width * img_size.height * 3];
#if 0
  //file >>> buff_UYVY
  {
    std::string   file_uyvy("uyvy422.raw");
    std::ifstream stream_uyvy;
    stream_uyvy.open(file_uyvy, std::ios::in | std::ios::binary);

    if (!stream_uyvy.is_open())
    {
      std::cerr << "[ERROR] cannot open the raw file " << file_uyvy
        << std::endl;
      std::cerr << std::endl;
      assert(0);
    }
    stream_uyvy.read((char*)buff_UYVY, img_size.width*img_size.height*2);
    stream_uyvy.close();
  }
#endif
  // create synthetic R,G,B,Black image
  unsigned char r,g,b;
  for (int i = 0; i < img_size.height; i++) 
    for (int j = 0; j < img_size.width; j+=3) {
      if (j < img_size.width/3) {r = 200; g = 0; b = 0;}
      else if (j < img_size.width*2/3) {r = 0; g = 200; b = 0;}
      else {r = 0; g = 0; b = 200;}
      buff_RGB[i*img_size.width*3+j] = r;
      buff_RGB[i*img_size.width*3+j+1] = g;
      buff_RGB[i*img_size.width*3+j+2] = b;}
  Npp8u* gpu_buff_UYVY;
  Npp8u* gpu_buff_RGB;
  // use NPP to convert synthetic RGB image into NPP format YUV422
  cudaError_t err_cu_api;
  err_cu_api = cudaMalloc((void**)&gpu_buff_UYVY,
                            img_size.width*img_size.height*2);
  std::cout << "cudaMalloc1 : " << err_cu_api << std::endl;
  err_cu_api = cudaMalloc((void**)&gpu_buff_RGB,
                            img_size.width*img_size.height*3);
  std::cout << "cudaMalloc3 : " << err_cu_api << std::endl;
  err_cu_api = cudaMemcpy((void*)gpu_buff_RGB,
                            (const void*)buff_RGB,
                            img_size.width*img_size.height*3,
                            cudaMemcpyHostToDevice);
  std::cout << "cudaMemcpy2 : " << err_cu_api << std::endl;
  NppiSize roi = {img_size.width, img_size.height};
  NppStatus status = nppiRGBToYUV422_8u_C3C2R(gpu_buff_RGB,
                                                img_size.width*3,
                                                gpu_buff_UYVY,
                                                img_size.width*2,
                                                roi);
  std::cout << "NppStatus : " << status << std::endl;
  err_cu_api = cudaMemcpy((void*) buff_UYVY,
                            (const void*)gpu_buff_UYVY,
                            img_size.width*img_size.height*2,
                            cudaMemcpyDeviceToHost);
  std::cout << "cudaMemcpy4 : " << err_cu_api << std::endl;
  // convert NPP format YUV422 to UYVY for use by OpenCV
  for (int i = 0; i < img_size.width*img_size.height*2; i+=2){
    unsigned char v1 = buff_UYVY[i];
    unsigned char v2 = buff_UYVY[i+1];
    buff_UYVY[i+1] = v1;
    buff_UYVY[i] = v2;}
  //buff_UYVY >>> mat_bgr
  cv::Mat mat_UYVY(img_size, CV_8UC2, buff_UYVY);
  cv::Mat mat_bgr(img_size, CV_8UC3);
  cv::cvtColor(mat_UYVY, mat_bgr, cv::COLOR_YUV2BGR_UYVY);
  cv::imshow("BGR Image from CPU", mat_bgr);
  cv::imwrite("mat_bgr.bmp", mat_bgr);
  cv::imwrite("mat_bgr.jpg", mat_bgr);
  //convert UYVY OpenCV format back to NPP YUV422 format for use by NPP
  for (int i = 0; i < img_size.width*img_size.height*2; i+=2){
    unsigned char v1 = buff_UYVY[i];
    unsigned char v2 = buff_UYVY[i+1];
    buff_UYVY[i+1] = v1;
    buff_UYVY[i] = v2;}


  //buff_UYVY >>> buff_RGB
  {
    err_cu_api = cudaMemcpy((void*)gpu_buff_UYVY,
                            (const void*)buff_UYVY,
                            img_size.width*img_size.height*2,
                            cudaMemcpyHostToDevice);
    std::cout << "cudaMemcpy4 : " << err_cu_api << std::endl;


    status = nppiYUV422ToRGB_8u_C2C3R(gpu_buff_UYVY,
                                                img_size.width*2,
                                                gpu_buff_RGB,
                                                img_size.width*3,
                                                roi);
    std::cout << "NppStatus : " << status << std::endl;

    err_cu_api = cudaMemcpy((void*) buff_RGB,
                            (const void*)gpu_buff_RGB,
                            img_size.width*img_size.height*3,
                            cudaMemcpyDeviceToHost);
    std::cout << "cudaMemcpy4 : " << err_cu_api << std::endl;
  }

  cv::Mat mat_rgb(img_size, CV_8UC3, buff_RGB);
//cv::cvtColor(mat_rgb, mat_rgb, cv::COLOR_RGB2BGR);

  std::cout << "depth : " << mat_rgb.depth() << std::endl;
  std::cout << "channels : " << mat_rgb.channels() << std::endl;
  std::cout << "elemSize : " << mat_rgb.elemSize() << std::endl;
  std::cout << "step1 : " << mat_rgb.step1() << std::endl;
  std::cout << "type : " << mat_rgb.type() << std::endl;

  try {
    cv::imshow("RGB Image from GPU", mat_rgb);
    cv::imwrite("mat_rgb.bmp", mat_rgb);
    cv::imwrite("mat_rgb.jpg", mat_rgb);
  } catch( cv::Exception& e ) {
    const char* err_msg = e.what();
    std::cout << "exception caught #2: " << err_msg << std::endl;
  }

//  cv::waitKey(0);

  delete[] buff_UYVY;
  delete[] buff_RGB;

  return 0;
}
(base) [bob@localhost misc]$ nvcc -o t30 t30.cu -lnppicc -lopencv_core -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc
(base) [bob@localhost misc]$ ./t30
cudaMalloc1 : 0
cudaMalloc3 : 0
cudaMemcpy2 : 0
NppStatus : 0
cudaMemcpy4 : 0
cudaMemcpy4 : 0
NppStatus : 0
cudaMemcpy4 : 0
depth : 0
channels : 3
elemSize : 3
step1 : 5760
type : 16

输出文件是这样的:

mat_rgb.png:

mat_bgr.png:

主要区别是您已经知道的 RGB <-> BGR 不匹配。

对于剩余的颜色差异,也可能是 OpenCV 的 YUV 色彩空间与 NPP 的色彩空间不太匹配。如果您愿意,可以在 NPP 中尝试其他 YUV 函数,例如 nppiCbYCr422ToBGR_709HDTV_8u_C2C3R.