想要一些关于如何使用 nvjpegEncodeYUV() 的指南

Want some guide about how to use nvjpegEncodeYUV()

我正在尝试基于以下示例代码实现一些 jpeg 编码 cuda 代码: https://docs.nvidia.com/cuda/nvjpeg/index.html#nvjpeg-encode-examples

我post所有的代码,测试结果和Makefile和输入文件。

我正在使用 NVIDIA 2080 HW + cuda 11.2 进行测试

但是从这道题可以看出,结果是fail。第一次失败是从 nvjpegEncodeYUV() 返回的错误代码是 2。抱怨一些无效参数。 但是我不知道哪里出了问题....你能帮我找出问题所在吗?

#include <iterator>
#include <fstream>
#include <iostream>
#include <vector>

#include <cassert>
#include <unistd.h>

#include "nppdefs.h"
#include "nppi_support_functions.h"
#include "nppi_color_conversion.h"
#include "nvjpeg.h"

#include "cuda_runtime.h"


#define DEFAULT_RAWFILE  "./uyvy422.raw"

//file >>> buff_UYVY
int read_raw(const char *file2read, unsigned char *buff_UYVY)
{
  if (!file2read) {
    std::cout << "file2read empty!!" << std::endl;
    return 1;
  }

  if (!buff_UYVY) {
    std::cout << "buff_UYVY empty!!" << std::endl;
    return 1;
  }

  std::string   file_uyvy(file2read);
  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, 1920*1080*2);
  stream_uyvy.close();

  return 0;
}


int main(int argc, char*argv[])
{
  unsigned char *buff_UYVY =
    new unsigned char[1920 * 1080 * 2];

  //file >>> buff_UYVY
  int ret;
  if (argv[1]) {
    ret = read_raw(argv[1], buff_UYVY);
  } else {
    ret = read_raw(DEFAULT_RAWFILE, buff_UYVY);
  }
  if (ret != 0) {
    std::cout << "read_raw() failed!!" << std::endl;
    return ret;
  }
  if (!buff_UYVY) {
    std::cout << "buff_UYVY empty!!" << std::endl;
    return 1;
  }

  cudaError_t err_cu_api;
  Npp8u* gpu_buff_CbYCr422;

  err_cu_api = cudaMalloc((void**)&gpu_buff_CbYCr422,
                          1920*1080*2);
  err_cu_api = cudaMemcpy((void*)gpu_buff_CbYCr422,
                          (const void*)buff_UYVY,
                          1920*1080*2,
                          cudaMemcpyHostToDevice);

  //////////////////////////////////////////////////////////////////////////////
  //https://docs.nvidia.com/cuda/nvjpeg/index.html#nvjpeg-encode-examples

  nvjpegStatus_t status;
  nvjpegHandle_t nv_handle;
  nvjpegEncoderState_t nv_enc_state;
  nvjpegEncoderParams_t nv_enc_params;
  cudaStream_t stream = 0;

  // initialize nvjpeg structures
  status = nvjpegCreateSimple(&nv_handle);
  std::cout << "nvjpegCreateSimple : " << status << std::endl;
  status = nvjpegEncoderStateCreate(nv_handle, &nv_enc_state, stream);
  std::cout << "nvjpegEncoderStateCreate : " << status << std::endl;
  status = nvjpegEncoderParamsCreate(nv_handle, &nv_enc_params, stream);
  std::cout << "nvjpegEncoderParamsCreate : " << status << std::endl;

  nvjpegImage_t imgdesc =
  {
    {
      gpu_buff_CbYCr422,
      gpu_buff_CbYCr422 + 1920*1080,
      gpu_buff_CbYCr422 + 1920*1080*2,
      gpu_buff_CbYCr422 + 1920*1080*3
    },
    {
      1920,
      1920,
      1920,
      1920
    }
  };

  // Compress image
  status = nvjpegEncodeYUV(nv_handle, nv_enc_state, nv_enc_params,
                  &imgdesc, NVJPEG_CSS_422, 1920, 1080,
                  stream);
  std::cout << "nvjpegEncodeYUV : " << status << std::endl;

  // get compressed stream size
  size_t length;
  status = nvjpegEncodeRetrieveBitstream(nv_handle, nv_enc_state, NULL,
                                                &length, stream);
  std::cout << "nvjpegEncodeRetrieveBitstream : " << status << std::endl;
  // get stream itself
  cudaStreamSynchronize(stream);
  std::vector<char> jpeg(length);
  status = nvjpegEncodeRetrieveBitstream(nv_handle, nv_enc_state,
                                (unsigned char*)jpeg.data(), &length, 0);
  std::cout << "nvjpegEncodeRetrieveBitstream : " << status << std::endl;

  // write stream to file
  cudaStreamSynchronize(stream);
  std::ofstream output_file("test.jpg", std::ios::out | std::ios::binary);
  output_file.write(jpeg.data(), length);
  output_file.close();

  //https://docs.nvidia.com/cuda/nvjpeg/index.html#nvjpeg-encode-examples
  //////////////////////////////////////////////////////////////////////////////

  cudaFree(gpu_buff_CbYCr422);
  err_cu_api = err_cu_api;

  delete[] buff_UYVY;

  return 0;
}
$ ./test
nvjpegCreateSimple : 0
nvjpegEncoderStateCreate : 0
nvjpegEncoderParamsCreate : 0
nvjpegEncodeYUV : 2
nvjpegEncodeRetrieveBitstream : 2
nvjpegEncodeRetrieveBitstream : 2
CC = g++
CFLAGS = -v -Wall -I/usr/local/cuda/include -g
LDFLAGS += -L/usr/local/cuda/lib64
SRCS = main_gpu.cpp
PROG = test

OPENCV = `pkg-config opencv4 --cflags --libs`
LIBS = $(OPENCV) \
       -lcudart \
       -lnppisu \
       -lnpps \
       -lnppc \
       -lnppial \
       -lnppicc \
       -lnppidei \
       -lnppif \
       -lnppig \
       -lnppim \
       -lnppist \
       -lnppitc \
       -lnvjpeg

.PHONY: all clean

all: $(PROG)

$(PROG):$(SRCS)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROG) $(SRCS) $(LIBS)

clean:
        rm -f $(OBJS) $(PROG) *.jpg *.bmp

正在获取输入文件:

git clone https://github.com/jumogehn/Jumogehn.git

uyvy422.raw是吗

根据我在你的代码中看到的内容,我猜你的输入存储格式是 ordinary YUV422:

U0 Y0 V0 Y1 U2 Y2 V2 Y3 U4 Y4 V4…

这是一种交错存储格式。但是 docs for nvjpegEncodeYUV 状态:

The source argument should be filled with the corresponding YUV planar data.

因此您需要将交错输入转换为 planar storage of a Y plane followed by a U plane followed by a V plane

因此,您的 imgdesc 需要更改,因为 U 和 V 平面的间距是 Y 平面的一半:

  nvjpegImage_t imgdesc =
  {
    {
      gpu_buff_CbYCr422,                        // pointer to start of Y plane
      gpu_buff_CbYCr422 + 1920*1080,                 // pointer to start of U plane
      gpu_buff_CbYCr422 + 1920*1080 + 960*1080,  // pointer to start of V plane
      NULL
    },
    {
      1920,                         // pitch of Y plane
      960,                           // pitch of U plane
      960,                          // pitch of V plane
      0
    }
  };

最后,您需要在参数中设置采样因子:

$ cat t2017.cpp
#include "nvjpeg.h"

#include "cuda_runtime.h"
#include <iostream>

#define DEFAULT_RAWFILE  "./uyvy422.raw"

int main(int argc, char*argv[])
{
  unsigned char *buff_UYVY =
    new unsigned char[1920 * 1080 * 2];

  cudaError_t err_cu_api;
  unsigned char* gpu_buff_CbYCr422;

  err_cu_api = cudaMalloc((void**)&gpu_buff_CbYCr422,
                          1920*1080*2);
  err_cu_api = cudaMemcpy((void*)gpu_buff_CbYCr422,
                          (const void*)buff_UYVY,
                          1920*1080*2,
                          cudaMemcpyHostToDevice);

  //////////////////////////////////////////////////////////////////////////////
  //https://docs.nvidia.com/cuda/nvjpeg/index.html#nvjpeg-encode-examples

  nvjpegStatus_t status;
  nvjpegHandle_t nv_handle;
  nvjpegEncoderState_t nv_enc_state;
  nvjpegEncoderParams_t nv_enc_params;
  cudaStream_t stream = 0;

  // initialize nvjpeg structures
  status = nvjpegCreateSimple(&nv_handle);
  std::cout << "nvjpegCreateSimple : " << status << std::endl;
  status = nvjpegEncoderStateCreate(nv_handle, &nv_enc_state, stream);
  std::cout << "nvjpegEncoderStateCreate : " << status << std::endl;
  status = nvjpegEncoderParamsCreate(nv_handle, &nv_enc_params, stream);
  std::cout << "nvjpegEncoderParamsCreate : " << status << std::endl;

  nvjpegImage_t imgdesc =
  {
    {
      gpu_buff_CbYCr422,
      gpu_buff_CbYCr422 + 1920*1080,
      gpu_buff_CbYCr422 + 1920*1080 + 960*1080,
      NULL
    },
    {
      1920,
      960,
      960,
      0
    }
  };
  status = nvjpegEncoderParamsSetSamplingFactors(nv_enc_params, NVJPEG_CSS_422, stream);
  std::cout << "nvjpegEncoderParamsSetSamplingFactors: " << status << std::endl;
  // Compress image
  status = nvjpegEncodeYUV(nv_handle, nv_enc_state, nv_enc_params,
                  &imgdesc, NVJPEG_CSS_422, 1920, 1080,
                  stream);
  std::cout << "nvjpegEncodeYUV : " << status << std::endl;
}
$ g++ t2017.cpp -I/usr/local/cuda/include -L/usr/local/cuda/lib64 -lnvjpeg -lcudart -o t2017
$ ./t2017
nvjpegCreateSimple : 0
nvjpegEncoderStateCreate : 0
nvjpegEncoderParamsCreate : 0
nvjpegEncoderParamsSetSamplingFactors: 0
nvjpegEncodeYUV : 0
$

我并不是说这会修复代码中所有可能的错误,只是它似乎解决了这个问题:

First fail is from nvjpegEncodeYUV() The returned error code is 2. That complains for some invalid parameters. But I have no idea what is wrong.... Could you please help me find what went wrong?