nvImageCodec examples

[1]:
import os
import cv2
import numpy as np
from matplotlib import pyplot as plt

Setting resource folder

[2]:
resources_dir = os.getenv("PYNVIMGCODEC_EXAMPLES_RESOURCES_DIR", "../assets/images/")

Import nvImageCodec module and create Decoder and Encoder

[3]:
from nvidia import nvimgcodec
decoder = nvimgcodec.Decoder()
encoder = nvimgcodec.Encoder()

Load and decode Jpeg image with nvImageCodec

[4]:
with open(resources_dir + "tabby_tiger_cat.jpg", 'rb') as in_file:
    data = in_file.read()
    nv_img_cat = decoder.decode(data)

Save image to bmp file with nvImageCodec

[5]:
with open("cat-jpg-o.bmp", 'wb') as out_file:
    data = encoder.encode(nv_img_cat, "bmp")
    out_file.write(data)

Read back with OpenCV just saved (with nvImageCodec) bmp image

[6]:
cv_img_bmp = cv2.imread("cat-jpg-o.bmp")
cv_img_bmp = cv2.cvtColor(cv_img_bmp, cv2.COLOR_BGR2RGB)
plt.imshow(cv_img_bmp)
[6]:
<matplotlib.image.AxesImage at 0x7feee818ad90>
../_images/samples_nvimgcodec_11_1.png

Load and decode Jpeg2000 (in jp2 container) image with nvImageCodec in one read function

[7]:
nv_img = decoder.read(resources_dir + "cat-1046544_640.jp2")

Save image to jpg file with nvImageCodec in one write function

[8]:
encoder.write("cat-jp2-o.jpg", nv_img)

Read back with OpenCV just save (with nvImageCodec) bmp image

[9]:
image = cv2.imread("cat-jp2-o.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
[9]:
<matplotlib.image.AxesImage at 0x7feed40b8640>
../_images/samples_nvimgcodec_17_1.png

Load jpg with nvImageCodec

[10]:
nv_img_jpg = decoder.read(resources_dir + "tabby_tiger_cat.jpg")

Save as Jpeg 2000 with nvImageCodec

[11]:
encoder.write("cat-jpg-o.j2k", nv_img_jpg)

Read back with OpenCV just saved (with nvImageCodec) j2k image

[12]:
image = cv2.imread("cat-jpg-o.j2k")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
[12]:
<matplotlib.image.AxesImage at 0x7feed40a8fa0>
../_images/samples_nvimgcodec_23_1.png

Passing decoding parameters

Decode jpeg with Exif orientation - by default it applies exif orientation

[13]:
nv_img_jpg = decoder.read(resources_dir+ "f-exif-8.jpg")
encoder.write("f-exif-8.bmp", nv_img_jpg)
image = cv2.imread("f-exif-8.bmp")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
[13]:
<matplotlib.image.AxesImage at 0x7feed078f310>
../_images/samples_nvimgcodec_26_1.png

Let assume we would like to ignore exif orientation

[14]:
dec_params = nvimgcodec.DecodeParams(apply_exif_orientation=False)
nv_img_jpg = decoder.read(resources_dir + "f-exif-8.jpg", dec_params)
encoder.write("f-wo-exif.bmp", nv_img_jpg)
image = cv2.imread("f-wo-exif.bmp")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
[14]:
<matplotlib.image.AxesImage at 0x7feed0706580>
../_images/samples_nvimgcodec_28_1.png

Passing encoding parameters

Changing quality and chroma subsampling in jpeg

[15]:
nv_img_jpg = decoder.read(resources_dir + "tabby_tiger_cat.jpg")
enc_params = nvimgcodec.EncodeParams(quality=5, chroma_subsampling=nvimgcodec.ChromaSubsampling.CSS_GRAY)
encoder.write("cat-q5-gray.jpg", nv_img_jpg, params=enc_params)

image = cv2.imread("cat-q5-gray.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
[15]:
<matplotlib.image.AxesImage at 0x7feed06efe20>
../_images/samples_nvimgcodec_31_1.png

Jpeg optimized huffman and progressive encoding

[16]:
nv_img_jpg = decoder.read(resources_dir + "tabby_tiger_cat.jpg")
encoder.write("cat-q75.jpg", nv_img_jpg, params=nvimgcodec.EncodeParams(quality=75))
encoder.write("cat-q75-optimized_huffman.jpg", nv_img_jpg, params=nvimgcodec.EncodeParams(
    quality=75, jpeg_encode_params = nvimgcodec.JpegEncodeParams(optimized_huffman=True, progressive=True)))

print("default huffman file size:", os.path.getsize("cat-q75.jpg"))
print("optimized huffman file size:", os.path.getsize(
    "cat-q75-optimized_huffman.jpg"))

image = cv2.imread("cat-q75-optimized_huffman.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)

default huffman file size: 69743
optimized huffman file size: 66706
[16]:
<matplotlib.image.AxesImage at 0x7feed061ce20>
../_images/samples_nvimgcodec_33_2.png

Encode lossless and lossy with jpeg2000

[17]:
import ctypes
nv_img_jpg = decoder.read(resources_dir + "tabby_tiger_cat.jpg")

encoder.write("cat-psnr25.j2k", nv_img_jpg, params=nvimgcodec.EncodeParams(target_psnr=25))

jpeg2k_encode_params = nvimgcodec.Jpeg2kEncodeParams(reversible=True)
encoder.write("cat-lossless.j2k", nv_img_jpg, params=nvimgcodec.EncodeParams(jpeg2k_encode_params=jpeg2k_encode_params))

jpeg2k_encode_params.num_resolutions = 2
jpeg2k_encode_params.code_block_size = (32, 32)
jpeg2k_encode_params.bitstream_type = nvimgcodec.Jpeg2kBitstreamType.JP2
jpeg2k_encode_params.prog_order = nvimgcodec.Jpeg2kProgOrder.LRCP
encoder.write("cat-lossless-2decomps.j2k", nv_img_jpg, params=nvimgcodec.EncodeParams(jpeg2k_encode_params=jpeg2k_encode_params))

print("lossy file size:", os.path.getsize("cat-psnr25.j2k"))
print("lossless file size:", os.path.getsize("cat-lossless.j2k"))
print("lossless 2 decomposition levels file size:",  os.path.getsize("cat-lossless-2decomps.j2k"))
image = cv2.imread("cat-psnr25.j2k")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
lossy file size: 2784
lossless file size: 598167
lossless 2 decomposition levels file size: 670806
[17]:
<matplotlib.image.AxesImage at 0x7feed059e100>
../_images/samples_nvimgcodec_35_2.png

We can specify allowed backends used for decoding

[18]:
gpu_dec = nvimgcodec.Decoder(backends=[nvimgcodec.Backend(nvimgcodec.GPU_ONLY, load_hint=0.5), nvimgcodec.Backend(nvimgcodec.HYBRID_CPU_GPU)])
cpu_dec = nvimgcodec.Decoder(backend_kinds=[nvimgcodec.CPU_ONLY])
[19]:
%%time
nv_img_j2k = cpu_dec.read(resources_dir + "cat-1046544_640.jp2")
CPU times: user 98.2 ms, sys: 699 µs, total: 98.9 ms
Wall time: 98.1 ms
[20]:
%%time
nv_img_j2k = gpu_dec.read(resources_dir + "cat-1046544_640.jp2")

CPU times: user 1.92 ms, sys: 345 µs, total: 2.26 ms
Wall time: 2.26 ms

The same way we can create Encoder with allowed backends.

[21]:
gpu_enc = nvimgcodec.Encoder(backends=[nvimgcodec.Backend(nvimgcodec.GPU_ONLY, load_hint=0.5)
                             , nvimgcodec.Backend(nvimgcodec.HYBRID_CPU_GPU)])
cpu_enc = nvimgcodec.Encoder(backend_kinds=[nvimgcodec.CPU_ONLY])
[22]:
gpu_enc.write("cat_gpu_out.jpg", nv_img_j2k)

Currently there is no CPU encoder available for jpeg so having cpu_enc we can write for example to bmp

[23]:
cpu_enc.write("cat_cpu_out.bmp", nv_img_j2k)

Support of __cuda_array_interface__

[24]:
print(nv_img_j2k.__cuda_array_interface__)
print(nv_img_j2k.shape)
{'shape': (475, 640, 3), 'strides': None, 'typestr': '|u1', 'data': (12924947456, False), 'version': 3, 'stream': 1}
(475, 640, 3)

Support of __array_interface__

It is possible to pass to nvImageCodec host ndarray by object which supports __array_interface__ as for example image (numpy.ndarray) created by OpenCV

[25]:
cv_img = cv2.imread(resources_dir + "Weimaraner.bmp")
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)

print(type(cv_img))
print(cv_img.__array_interface__)

nv_h_img = nvimgcodec.as_image(cv_img)
gpu_enc.write("Weimaraner_ai_out.jpg", nv_h_img)

image = cv2.imread("Weimaraner_ai_out.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)

<class 'numpy.ndarray'>
{'data': (114624928, False), 'strides': None, 'descr': [('', '|u1')], 'typestr': '|u1', 'shape': (720, 720, 3), 'version': 3}
[25]:
<matplotlib.image.AxesImage at 0x7feed052d5b0>
../_images/samples_nvimgcodec_49_2.png

If we use cpu() method of Image object it would create new Image with copied content to host buffer.

[26]:
nv_img = cpu_dec.read(resources_dir + "cat-1046544_640.jp2")
nv_h_img = nv_img.cpu()

Image with host buffer supports __array_interface__ (but can’t return a proper __cuda_array_interface__)

[27]:
print(nv_h_img.__array_interface__)
print(nv_h_img.__cuda_array_interface__)
{'shape': (475, 640, 3), 'strides': None, 'typestr': '|u1', 'data': (140658298579968, False), 'version': 3}
{}

so we can pass such Image to functions which accept and can use this interface like imshow from matplotlib library

[28]:
plt.imshow(nv_h_img)
[28]:
<matplotlib.image.AxesImage at 0x7feed04af0d0>
../_images/samples_nvimgcodec_55_1.png

We can also create a zero-copy view of this image with numpy and process it with OpenCV

[29]:
np_img = np.asarray(nv_h_img)
kernel = np.ones((5, 5), np.float32)/25
dst = cv2.filter2D(np_img, -1, kernel)
plt.subplot(121), plt.imshow(np_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(dst), plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

../_images/samples_nvimgcodec_57_0.png

There is also method cuda() which can be used to convert an Image with a host buffer to an Image with copied contents to a device buffer.

[30]:
print(nv_h_img.__cuda_array_interface__)
nv_new_cuda_img = nv_h_img.cuda()
print(nv_new_cuda_img.__cuda_array_interface__)

{}
{'shape': (475, 640, 3), 'strides': None, 'typestr': '|u1', 'data': (12920011776, False), 'version': 3, 'stream': 1}

We can check whether Image keeps a host or a device buffer by reading the buffer_kind property

[31]:
print("Host image buffer kind: ", nv_h_img.buffer_kind)
print("Device image buffer kind: ", nv_new_cuda_img.buffer_kind)
Host image buffer kind:  ImageBufferKind.STRIDED_HOST
Device image buffer kind:  ImageBufferKind.STRIDED_DEVICE

Managing lifetime of decoder resources using “with” statement

[32]:
with nvimgcodec.Decoder() as decoder_2:
    nv_img = decoder_2.read(resources_dir + "cat-1046544_640.jp2")
    plt.imshow(nv_img.cpu())
../_images/samples_nvimgcodec_63_0.png

Similarly for encoder resources

[33]:
with nvimgcodec.Encoder() as encoder_2:
    encoder_2.write("cat-1046544_640_out.jpg", nv_img)
    image = cv2.imread("cat-1046544_640_out.jpg")
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.imshow(image)
../_images/samples_nvimgcodec_65_0.png