CUDA库之nvjpeg(一):入门介绍



一、介绍

1.1 nvJPEG解码器

nvJPEG 库提供了高性能,GPU加速的JPEG图像格式的解码函数,并且普遍应用在深度学习领域和超大规模的多媒体应用中。

这个库提供单张图或者多张图同时解码的能力,可以充分利用GPU资源和优化效率,并且使用者也可以管理需要解码的内存,灵活性还是比较强的。

nvJPEG大体上依次使用以下函数:

  • 使用JPEG图像数据流作为输入
  • 从数据流中获取图像的宽和高
  • 使用以上获取的信息来管理GPU内存并执行解码操作

nvJPEG提供了专用的API,用于从原始JPEG图像数据流中检索图像信息。

二、JPEG解码

nvJPEG库提供了单张图像解码和批量解码的函数

2.1 单张图像解码

先加载图像文件,以及图像文件的大小,输出就是解码后的buffer
以下是执行步骤

  • step0. 加载图像数据
	std::string strFileName = "./input_images/img2.jpg";
	// Read an image from disk.
	std::ifstream input(strFileName.c_str(),
		std::ios::in | std::ios::binary | std::ios::ate);
	if (!(input.is_open())) {
		std::cerr << "Cannot open image: " << strFileName
			<< ", removing it from image list" << std::endl;
	}
	// Get the size
	std::streamsize file_size = input.tellg();
	input.seekg(0, std::ios::beg);

	 char* pchData = (char*)malloc(file_size);
	if (!input.read(pchData, file_size)) {
		std::cerr << "Cannot read from file: " << strFileName
			<< ", removing it from image list" << std::endl;

	}
  • step1. 创建nvJPEG库的句柄(有两个函数nvjpegCreateSimple() or nvjpegCreateEx()
	nvjpegHandle* nvjpegHandle = nullptr;
	CHECK_NVJPEG(nvjpegCreateSimple(&nvjpegHandle));
  • step2. 调用nvjpegJpegStateCreate()函数,创建nvJPEG的状态
	nvjpegJpegState* nvjpegJpegState = nullptr;
	CHECK_NVJPEG(nvjpegJpegStateCreate(nvjpegHandle, &nvjpegJpegState));

除了以上外,还有其他的一些函数,也是有帮助的,这里先列举,可暂时忽略

nvjpegStatus_t nvjpegGetProperty(libraryPropertyType type, int *value);

[DEPRECATED] nvjpegStatus_t nvjpegCreate(nvjpegBackend_t backend, nvjpegHandle_t *handle , nvjpeg_dev_allocator allocator);

nvjpegStatus_t nvjpegCreateSimple(nvjpegHandle_t *handle);

nvjpegStatus_t nvjpegCreateEx(nvjpegBackend_t backend, nvjpegDevAllocator_t *dev_allocator, nvjpegPinnedAllocator_t *pinned_allocator, unsigned int flags, nvjpegHandle_t *handle);

nvjpegStatus_t nvjpegDestroy(nvjpegHandle_t handle);

nvjpegStatus_t nvjpegJpegStateCreate(nvjpegHandle_t handle, nvjpegJpegState_t *jpeg_handle);

nvjpegStatus_t nvjpegJpegStateDestroy(nvjpegJpegState handle);
  • step3. 使用nvjpegGetImageInfo()从数据流中获取获取宽高
    函数的参数如下:
nvjpegStatus_t nvjpegGetImageInfo(
nvjpegHandle_t              handle, // 句柄
const unsigned char         *data,	// 文件数据
size_t                      length, // 文件长度
int                         *nComponents, // 通道
nvjpegChromaSubsampling_t   *subsampling, // 色度的二次采样信息
int                         *widths, // 宽
int                         *heights); // 高

调用案例如下:

	int channels = 0;
	int width = 0;
	int height = 0;
	nvjpegChromaSubsampling_t subsampling;
	CHECK_NVJPEG(nvjpegGetImageInfo(nvjpegHandle, (const unsigned char*)pchData, file_size, &channels, &subsampling, &width, &height));
  • step4. 使用 nvjpegDecode()函数来解码单张JPEG图像,函数介绍如下:
nvjpegStatus_t nvjpegDecode(
nvjpegHandle_t          handle, // 库句柄
nvjpegJpegState_t       jpeg_handle, //状态句柄
const unsigned char     *data, // 输入的文件数据
size_t                  length, // 输入的文件数据长度
nvjpegOutputFormat_t    output_format,// 解码格式
nvjpegImage_t           *destination, // 输出解码后的buffer信息
cudaStream_t            stream); // 流

这里注意,nvjpegImage_t里的内存,需要自己管理和创建

	cudaStream_t stream;

	CHECK_CUDA(cudaStreamCreate(&stream));

	nvjpegImage_t dstImage;
	memset(&dstImage, 0, sizeof(nvjpegImage_t));
	// realloc output buffer if required
	int mul = 3;
	channels = 1;
	for (int c = 0; c < channels; c++) {
		int aw = mul * width;
		int ah = height;
		int sz = aw * ah;
		dstImage.pitch[c] = aw;
		//if (sz > dstImage.pitch[c]) {
		/*	if (dstImage.channel[c]) {
				CHECK_CUDA(cudaFree(dstImage.channel[c]));
			}*/
			CHECK_CUDA(cudaMalloc((void**)&dstImage.channel[c], sz));
			//dstImage.pitch[c] = sz;
		//}
	}

	nvjpegOutputFormat_t outFormat = nvjpegOutputFormat_t::NVJPEG_OUTPUT_BGRI;
	CHECK_NVJPEG(nvjpegDecode(nvjpegHandle, nvjpegJpegState, (const unsigned char*)pchData, file_size,
		outFormat, &dstImage, stream));
	//CHECK_CUDA(cudaStreamSynchronize(stream));
	int sz = height * width * sizeof(unsigned char);
	unsigned char* pvB = (unsigned char*)malloc(sz *3);
	/*unsigned char* pvG = (unsigned char*)malloc(sz);
	unsigned char* pvR = (unsigned char*)malloc(sz);*/
	CHECK_CUDA(cudaMemcpy(pvB, dstImage.channel[0], sz*3, cudaMemcpyDeviceToHost));
	//CHECK_CUDA(cudaMemcpy(pvG, dstImage.channel[1], sz, cudaMemcpyDeviceToHost));
	//CHECK_CUDA(cudaMemcpy(pvR, dstImage.channel[2], sz, cudaMemcpyDeviceToHost));
	cv::Mat B = cv::Mat(cv::Size(width, height), CV_8UC3, pvB);
	//cv::Mat G = cv::Mat(cv::Size(width, height), CV_8UC1, pvG);
	//cv::Mat R = cv::Mat(cv::Size(width, height), CV_8UC1, pvR);
	cv::imwrite("H:/temp/rgb.bmp", B);

三、完整代码

注意,没有考虑现存的释放等操作,仅参考用

#include <iostream>
#include <fstream>
#include "opencv2/opencv.hpp"
#include "nvjpegDecoder.h"
int main()
{
	nvjpegHandle* nvjpegHandle = nullptr;
	CHECK_NVJPEG(nvjpegCreateSimple(&nvjpegHandle));

	nvjpegJpegState* nvjpegJpegState = nullptr;
	CHECK_NVJPEG(nvjpegJpegStateCreate(nvjpegHandle, &nvjpegJpegState));

	std::string strFileName = "H:/github/CUDALibrarySamples/nvJPEG/nvJPEG-Decoder/input_images/img2.jpg";
	// Read an image from disk.
	std::ifstream input(strFileName.c_str(),
		std::ios::in | std::ios::binary | std::ios::ate);
	if (!(input.is_open())) {
		std::cerr << "Cannot open image: " << strFileName
			<< ", removing it from image list" << std::endl;
	}
	// Get the size
	std::streamsize file_size = input.tellg();
	input.seekg(0, std::ios::beg);

	 char* pchData = (char*)malloc(file_size);
	if (!input.read(pchData, file_size)) {
		std::cerr << "Cannot read from file: " << strFileName
			<< ", removing it from image list" << std::endl;

	}

	int channels = 0;
	int width = 0;
	int height = 0;
	nvjpegChromaSubsampling_t subsampling;
	CHECK_NVJPEG(nvjpegGetImageInfo(nvjpegHandle, (const unsigned char*)pchData, file_size, &channels, &subsampling, &width, &height));
	cudaStream_t stream;

	CHECK_CUDA(cudaStreamCreate(&stream));

	nvjpegImage_t dstImage;
	memset(&dstImage, 0, sizeof(nvjpegImage_t));
	// realloc output buffer if required
	int mul = 3;
	channels = 1;
	for (int c = 0; c < channels; c++) {
		int aw = mul * width;
		int ah = height;
		int sz = aw * ah;
		dstImage.pitch[c] = aw;
		//if (sz > dstImage.pitch[c]) {
		/*	if (dstImage.channel[c]) {
				CHECK_CUDA(cudaFree(dstImage.channel[c]));
			}*/
			CHECK_CUDA(cudaMalloc((void**)&dstImage.channel[c], sz));
			//dstImage.pitch[c] = sz;
		//}
	}

	nvjpegOutputFormat_t outFormat = nvjpegOutputFormat_t::NVJPEG_OUTPUT_BGRI;
	CHECK_NVJPEG(nvjpegDecode(nvjpegHandle, nvjpegJpegState, (const unsigned char*)pchData, file_size,
		outFormat, &dstImage, stream));
	//CHECK_CUDA(cudaStreamSynchronize(stream));
	int sz = height * width * sizeof(unsigned char);
	unsigned char* pvB = (unsigned char*)malloc(sz *3);
	/*unsigned char* pvG = (unsigned char*)malloc(sz);
	unsigned char* pvR = (unsigned char*)malloc(sz);*/
	CHECK_CUDA(cudaMemcpy(pvB, dstImage.channel[0], sz*3, cudaMemcpyDeviceToHost));
	//CHECK_CUDA(cudaMemcpy(pvG, dstImage.channel[1], sz, cudaMemcpyDeviceToHost));
	//CHECK_CUDA(cudaMemcpy(pvR, dstImage.channel[2], sz, cudaMemcpyDeviceToHost));
	cv::Mat B = cv::Mat(cv::Size(width, height), CV_8UC3, pvB);
	//cv::Mat G = cv::Mat(cv::Size(width, height), CV_8UC1, pvG);
	//cv::Mat R = cv::Mat(cv::Size(width, height), CV_8UC1, pvR);
	cv::imwrite("H:/temp/rgb.bmp", B);
	//cv::imwrite("H:/temp/g.bmp", G);
	//cv::imwrite("H:/temp/b.bmp", B);

	printf("hello world!");
	return 0;
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您可以通过以下步骤来搭建Harbor私有仓: 1. 安装Docker和Docker-compose 2. 下载Harbor镜像并启动容器 3. 访问Harbor Web界面并进行初始化设置 4. 创建用户帐户并登录 5. 推送和下载镜像 请注意,Harbor的安装和配置可能需要一些技术知识,并且应根据您的系统环境进行适当的调整。有关更详细的信息,请参阅Harbor官方文档。 ### 回答2: Docker是一项非常受欢迎的容器化技术,它使得应用开发和部署非常方便,而Harbor是一款优秀的开源Docker私有仓软件,它提供了安全、可靠、灵活的私有容器仓解决方案,是非常适合企业内部使用的仓软件。 搭建Harbor私有仓的过程可以分为以下几个步骤: 1. 安装Docker:在安装Harbor之前,你需要先安装好Docker,确保Docker已经正确安装、运行,并且网络已经配置好。 2. 下载并解压Harbor安装包:从Harbor的官方网站(https://goharbor.io/)下载Harbor的安装包,然后解压到指定目录中。 3. 配置Harbor:在解压后的目录中,找到harbor.cfg文件,按照需求修改该文件中的配置选项,例如端口、数据存储目录、认证方式、管理员密码等。 4. 启动Harbor:运行docker-compose up -d命令即可启动Harbor私有仓,启动后可以使用docker ps命令查看状态。 5. 配置Docker客户端:在启动Harbor后,你需要配置Docker客户端的认证信息,才能访问Harbor仓。在Docker客户端中使用docker login命令,输入用户名、密码和仓地址即可。 6. 使用Harbor:在配置完成后,你可以使用docker命令或者Harbor的Web UI界面来管理和使用你的私有仓了。 总之,在使用Harbor搭建私有仓时,需要先安装好Docker,并在运行Harbor之前进行配置,配置好认证信息,才能正常访问私有仓。Harbor还提供了丰富的权限管理、镜像管理、日志管理等功能,非常适合用于企业内部应用的私有容器仓解决方案。 ### 回答3: Docker是一种轻便的容器技术,Harbor是一种用于Docker注册表和管理系统的私有仓。Harbor私有仓可以极大地方便企业级应用程序的构建、部署和管理,特别适用于A DevOps和微服务架构。要搭建Harbor私有仓,步骤如下: 第一步,安装Docker和Docker Compose。在安装完Docker后,可以使用以下命令来安装Docker Compose: curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose 第二步,下载并安装Harbor私有仓。从Harbor的官方网站上下载和安装最新版本的Harbor软件包: wget https://api.github.com/repos/goharbor/harbor/releases/latest -O harbor.json RELEASE_VERSION=$(cat harbor.json|jq ".name"|sed 's/"//g') echo "Harbor Version: ${RELEASE_VERSION}" wget https://github.com/goharbor/harbor/releases/download/${RELEASE_VERSION}/harbor-online-installer-${RELEASE_VERSION}.tgz tar zxvf harbor-online-installer-${RELEASE_VERSION}.tgz -C /opt 第三步,创建并编辑Harbor配置文件。在Harbor的安装目录下,创建一个名为harbor.cfg的配置文件: cd /opt/harbor cp harbor.cfg.tmpl harbor.cfg vi harbor.cfg 在配置文件中,指定Harbor的主机名、端口和数据存储路径等细节。你可以调整这些设置来适应你的特定需求。 第四步,启动Harbor容器。使用Docker Compose和Harbor配置文件来创建Harbor的运行环境: docker-compose up -d 第五步,使用浏览器访问Harbor私有仓。在浏览器中访问http://localhost:8888,你会看到Harbor的登录页面。输入管理员账户和密码,即可进入Harbor管理界面。 第六步,使用Harbor私有仓。使用Docker Registry API或Docker客户端命令行工具,与你的Harbor私有仓进行交互。将部署在Harbor私有仓中的镜像下载并使用在你的应用程序中。 总之,搭建Harbor私有仓非常不错,它提供了一个安全、集中和可扩展的Docker仓,并支持自动构建和镜像扫描等高级功能。随着DevOps和微服务的兴起,Harbor私有仓将成为企业应用的关键组成部分,也值得我们深入探索和研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值