在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库
在NVIDIA A100 GPU上利用硬件JPEG解碼器和NVIDIA nvJPEG庫
根據調查,普通人產生的1.2萬億張圖像可以通過電話或數碼相機捕獲。這樣的圖像的存儲,尤其是以高分辨率的原始格式,會占用大量內存。
JPEG指的是聯合圖像專家組,該組織于2017年慶祝成立25周年。JPEG標準指定了編解碼器,該編解碼器定義了如何將圖像壓縮為字節的位流并解壓縮回圖像。
JPEG編解碼器的主要目的是最小化照片圖像文件的文件大小。JPEG是一種有損壓縮格式,這意味著它不存儲原始圖像的完整像素數據。JPEG的優點之一是,它可以微調所使用的壓縮量。如果正確使用,這會產生良好的圖像質量,同時還會使最小的合理文件大小成為可能。
JPEG壓縮的關鍵組件如下:
? 色彩空間轉換可以分離亮度(Y)和色度(Cb,Cr)分量。Cb和Cr的下采樣可以減小文件大小,而質量損失幾乎沒有引起注意,因為人的感知對這些圖像分量不太敏感。這不是核心標準的一部分,但是定義為JFIF格式的一部分。
? 基于塊的離散余弦變換(DCT)允許以較低的頻率壓縮數據。
? 量化允許對高頻細節進行舍入系數。通常,可以丟失這些細節,因為人眼通常無法輕松地區分高頻內容。
? 逐行編碼在部分解碼位流后預覽整個圖像的低質量版本。
以下照片(圖1)顯示了JPEG壓縮導致的圖像質量損失。原始的蝴蝶圖像為BMP格式(512×512,24位,769 kB,無壓縮),然后以JPEG格式顯示同一圖像,質量壓縮系數為50%,二次采樣為4:2:0,24位,圖片大小為33 KB。
圖1a. 原始蝴蝶圖像(無壓縮,大小512×512,24位),769 KB。
圖1b. 壓縮蝴蝶圖像(質量壓縮系數50%,二次采樣4:2:0,24位),33 KB。
JPEG如何運作
圖2顯示了JPEG編碼器的一種常見配置。
圖2.使用GPU CUDA軟件和CPU的并行利用的JPEG編碼過程圖。
首先,JPEG編碼以RGB彩色圖像開始。
第二步涉及將顏色轉換為代表亮度(亮度)的YCbCr顏色空間Y和代表色度(紅色和藍色投影)的Cb和Cr通道。然后,將Cb和Cr通道以預定因子(通常為2或3)進行下采樣。下采樣提供了壓縮的第一階段。
在下一階段,將每個通道劃分為8×8的塊,并計算DCT,這是一種類似于傅立葉變換的頻率空間變換。DCT本身是無損且可逆的,將一個8×8空間塊轉換為64個通道。
然后,對DCT系數進行量化,該過程是有損的并且包括第二壓縮階段。量化由JPEG質量參數控制,較低的質量設置對應于更嚴格的壓縮,因此文件更小。
量化閾值特定于每個空間頻率,并且已經過精心設計。對低頻的壓縮比對高頻的壓縮要少,因為人眼對大范圍內的細微W誤差比對高頻信號的大小變化更敏感。
最后階段是使用Huffman編碼無損地壓縮量化的DCT系數,并將其存儲在JPEG文件中,如圖2所示。
圖3顯示了NVIDIA GPU上的JPEG解碼過程。
圖3. JPEG解碼過程采用了GPU CUDA和軟件的并行利用。用于霍夫曼解碼的混合(CPU / GPU)方法克服了串行過程的停頓。
JPEG解碼過程從壓縮的JPEG位流開始,并提取頭信息。
然后,霍夫曼解碼處理串行過程,因為從比特流中一次解碼一個DCT系數。
下一步將解量化和逆DCT處理為8×8塊。
上采樣步驟處理YCbCr轉換并產生解碼的RGB圖像。
NVIDIA通過基于CUDA技術構建的nvJPEG庫加速了JPEG編解碼器。我們開發了JPEG算法的完整并行實現。JPEG編碼器和解碼器工作流的典型GPU加速部分如圖2和3所示。
新的JPEG硬件解碼器
推出了NVIDIA A100 GPU,它具有專用的硬件JPEG解碼器。以前,數據中心GPU上沒有這樣的硬件單元,JPEG解碼是同時使用CPU和GPU的純軟件CUDA解決方案。
現在,硬件解碼器與其余GPU同時運行,后者可以執行各種計算任務,例如圖像分類,目標檢測和圖像分割。與NVIDIA Tesla V100相比,它以不止一種方式提供了顯著的吞吐量提高,JPEG解碼速度提高了4-8倍。
它通過CUDA工具包的一部分nvJPEG庫公開。
nvJPEG庫概述
nvJPEG是用于JPEG編解碼器的GPU加速庫。結合數據擴展和圖像加載庫NVIDIA DALI,它可以通過加速數據的解碼和擴展來加速對圖像分類模型的深度學習訓練。A100包含5核硬件JPEG解碼引擎。nvJPEG利用硬件后端來批量處理JPEG圖像。
圖4. JPEG硬件解碼過程采用了硬件解碼器和GPU CUDA軟件的并行利用。硬件解碼器獨立于CUDA SM,因此可以同時使用軟件GPU解碼器。
通過選擇具有nvjpegCreateExinit功能的硬件解碼器,nvJPEG可提供基線JPEG解碼和各種顏色轉換格式(例如YUV 420、422、444)的加速。如圖4所示,與純CPU處理相比,這將使圖像解碼速度提高20倍。DALI的用戶可以直接受益于這種硬件加速,因為nvJPEG是抽象的。
nvJPEG庫支持以下操作:
? nvJPEG編碼
? nvJPEG轉碼
? nvJPEG解碼(包括硬件(A100)支持)
該庫支持以下JPEG選項:
? 基線和漸進JPEG編碼和解碼,僅適用于A100的基線解碼
? 每像素8位
? 霍夫曼比特流解碼
? 多達四個通道的JPEG位流
? 8位和16位量化表
? 以下三個色度通道Y,Cb,Cr(Y,U,V)的色度子采樣:
o 4:4:4
o 4:2:2
o 4:2:0
o 4:4:0
o 4:1:1
o 4:1:0
該庫具有以下功能:
? 同時使用CPU和GPU的混合解碼。
? 庫的輸入在主機內存中,輸出在GPU內存中。
? 單張圖像和批量圖像解碼。
? 用戶為設備提供的內存管理器和固定的主機內存分配。
績效數字
對于本節中的性能圖,使用了以下測試設置和GPU / CPU硬件:
? NVIDIA V100 GPU:CPU – E5-2698 v4 @ 2GHz 3.6GHz Turbo(Broadwell)HT on GPU – Tesla V100-SXM2-16GB(GV100)1 16160 MiB 1 80 SM GPU視頻時鐘1312 Batch 128和單線程
? NVIDIA A100 GPU CPU –鉑金8168 @ 2GHz 3.7GHz Turbo(Skylake)HT on GPU – A100-SXM4-40GB(GA100)1 40557 MiB 1108 SM GPU視頻時鐘1095 Batch 128和單線程
? CPU:CPU –鉑金8168 @ 2GHz 3.7GHz Turbo(Skylake)HT在TurboJPEG解碼上進行CPU測試
? 圖像數據集:2K FHD = 1920 x 1080 4K UHD = 3840 x 2160 CUDA Toolkit 11.0 CUDA驅動程序r450.24
接下來的兩個圖表顯示了硬件JPEG解碼器的解碼速度。
圖5.該圖顯示了A100上的硬件解碼比V100上的CUDA混合解碼所提高的速度。
圖6. V100上的混合解碼器所需的CPU線程數,以跟上A100上的硬件解碼器吞吐量。
通過將解碼工作轉移到硬件上,可以釋放寶貴的CPU周期,以便更好地利用它們。
圖7顯示了編碼加速。
圖7a. 對于1920×1080(2K FHD),3840×2160(4K UHD)圖像尺寸的CPU,CUDA(V100,A100)之間的JPEG基線編碼吞吐量比較。
圖7b. 對于1920×1080(2K FHD),3840×2160(4K UHD)圖像尺寸的CPU,CUDA(V100,A100)之間的JPEG漸進編碼吞吐量比較。
圖像解碼示例
這是使用nvJPEG庫的圖像解碼示例。此示例顯示了A100 GPU上硬件解碼器的使用以及其他NVIDIA GPU的后端回退。
//
// The following code example shows how to use the nvJPEG library for JPEG image decoding.
//
// Libraries used
// nvJPEG decoding
int main()
{
…
// create nvJPEG decoder and decoder state
nvjpegDevAllocator_t dev_allocator = {&dev_malloc, &dev_free};
nvjpegPinnedAllocator_t pinned_allocator ={&host_malloc, &host_free};
// Selecting A100 Hardware decoder
nvjpegStatus_t status = nvjpegCreateEx(NVJPEG_BACKEND_HARDWARE, &dev_allocator, &pinned_allocator,NVJPEG_FLAGS_DEFAULT, ¶ms.nvjpeg_handle);params.hw_decode_available = true;
if( status == NVJPEG_STATUS_ARCH_MISMATCH) {std::cout<<"Hardware Decoder not supported. Falling back to default backend"<<std::endl;
// GPU SW decoder selected
nvjpegCreateEx(NVJPEG_BACKEND_DEFAULT, &dev_allocator,&pinned_allocator, NVJPEG_FLAGS_DEFAULT,¶ms.nvjpeg_handle);
params.hw_decode_available = false;
}
// create JPEG decoder state
nvjpegJpegStateCreate(params.nvjpeg_handle, ¶ms.nvjpeg_state)// extract bitstream metadata to figure out whether a bitstream can be decoded
nvjpegJpegStreamParseHeader(params.nvjpeg_handle, (const unsigned char *)img_data[i].data(), img_len[i], params.jpeg_streams[0]);// decode Batch images
nvjpegDecodeBatched(params.nvjpeg_handle, params.nvjpeg_state, batched_bitstreams.data(), batched_bitstreams_size.data(), batched_output.data(), params.stream)...
}
$ git clone https://github.com/NVIDIA/CUDALibrarySamples.git
$ cd nvJPEG/nvJPEG-Decoder/
$ mkdir build
$ cd build
$ cmake …
$ make
// Running nvJPEG decoder
$ ./nvjpegDecoder -i …/input_images/ -o ~/tmp
Decoding images in directory: …/input_images/, total 12, batchsize 1
Processing: …/input_images/cat_baseline.jpg
Image is 3 channels.
Channel #0 size: 64 x 64
Channel #1 size: 64 x 64
Channel #2 size: 64 x 64
YUV 4:4:4 chroma subsampling
Done writing decoded image to file:/tmp/cat_baseline.bmp
Processing: …/input_images/img8.jpg
Image is 3 channels.
Channel #0 size: 480 x 640
Channel #1 size: 240 x 320
Channel #2 size: 240 x 320
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img8.bmp
Processing: …/input_images/img5.jpg
Image is 3 channels.
Channel #0 size: 640 x 480
Channel #1 size: 320 x 240
Channel #2 size: 320 x 240
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img5.bmp
Processing: …/input_images/img7.jpg
Image is 3 channels.
Channel #0 size: 480 x 640
Channel #1 size: 240 x 320
Channel #2 size: 240 x 320
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img7.bmp
Processing: …/input_images/img2.jpg
Image is 3 channels.
Channel #0 size: 480 x 640
Channel #1 size: 240 x 320
Channel #2 size: 240 x 320
YUV 4:2:0 chroma subsampling
Done writing decoded image to file: /tmp/img2.bmp
Processing: …/input_images/img4.jpg
Image is 3 channels.
Channel #0 size: 640 x 426
Channel #1 size: 320 x 213
Channel #2 size: 320 x 213
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img4.bmp
Processing: …/input_images/cat.jpg
Image is 3 channels.
Channel #0 size: 64 x 64
Channel #1 size: 64 x 64
Channel #2 size: 64 x 64
YUV 4:4:4 chroma subsampling
Done writing decoded image to file:/tmp/cat.bmp
Processing: …/input_images/cat_grayscale.jpg
Image is 1 channels.
Channel #0 size: 64 x 64
Grayscale JPEG
Done writing decoded image to file:/tmp/cat_grayscale.bmp
Processing: …/input_images/img1.jpg
Image is 3 channels.
Channel #0 size: 480 x 640
Channel #1 size: 240 x 320
Channel #2 size: 240 x 320
YUV 4:2:0 chroma subsampling
Done writing decoded image to file: /tmp/img1.bmp
Processing: …/input_images/img3.jpg
Image is 3 channels.
Channel #0 size: 640 x 426
Channel #1 size: 320 x 213
Channel #2 size: 320 x 213
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img3.bmp
Processing: …/input_images/img9.jpg
Image is 3 channels.
Channel #0 size: 640 x 480
Channel #1 size: 320 x 240
Channel #2 size: 320 x 240
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img9.bmp
Processing: …/input_images/img6.jpg
Image is 3 channels.
Channel #0 size: 640 x 480
Channel #1 size: 320 x 240
Channel #2 size: 320 x 240
YUV 4:2:0 chroma subsampling
Done writing decoded image to file:/tmp/img6.bmp
Total decoding time: 14.8286
Avg decoding time per image: 1.23571
Avg images per sec: 0.809248
Avg decoding time per batch: 1.23571
圖像大小調整示例
此圖像調整大小和加水印示例根據客戶端請求生成圖像的縮放版本。
圖8顯示了圖像調整大小和加水印的典型工作流程。
圖8.并行使用GPU軟件和CUDA的圖像大小調整和水印流水線。
下面的代碼示例演示如何調整圖像大小并在徽標圖像上添加水印。
//
// The following code example shows how to resize images and watermark them with a logo image.
//
// Libraries used
// nvJPEG decoding, NPP Resize, NPP watermarking, nvJPEG encoding
int main()
{
…
// nvJPEG decoder
nReturnCode = nvjpegDecode(nvjpeg_handle, nvjpeg_decoder_state, dpImage, nSize, oformat, &imgDesc, NULL);
// NPP image resize
st = nppiResize_8u_C3R_Ctx(imgDesc.channel[0], imgDesc.pitch[0], srcSize,
srcRoi, imgResize.channel[0], imgResize.pitch[0], dstSize, dstRoi,
NPPI_INTER_LANCZOS, nppStreamCtx);
st = nppiResize_8u_C3R_Ctx(imgDescW.channel[0], imgDescW.pitch[0], srcSizeW, srcRoiW,imgResizeW.channel[0], imgResizeW.pitch[0], dstSize, dstRoi, NPPI_INTER_LANCZOS, nppStreamCtx);// Alpha Blending watermarking
st = nppiAlphaCompC_8u_C3R_Ctx(imgResize.channel[0], imgResize.pitch[0], 255, imgResizeW.channel[0], imgResizeW.pitch[0], ALPHA_BLEND, imgResize.channel[0], imgResize.pitch[0], dstSize, NPPI_OP_ALPHA_PLUS, nppStreamCtx);// nvJPEG encoding
nvjpegEncodeImage(nvjpeg_handle, nvjpeg_encoder_state, nvjpeg_encode_params,&imgResize, iformat, dstSize.width, dstSize.height,NULL));...
}
$ git clone https://github.com/NVIDIA/CUDALibrarySamples.git
$ cd nvJPEG/Image-Resize-WaterMark/
$ mkdir build
$ cd build
$ cmake …
$ make
// Running Image resizer and watermarking
$ ./imageResizeWatermark -i …/input_images/ -o resize_images -q 85 -rw 512 -rh 512
總結
以上是生活随笔為你收集整理的在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nvJPEG Codec库
- 下一篇: 在NVIDIA A100 GPU中使用D