v0.10.0-beta.11#

This software is in beta version, which means it is still undergoing testing and development before its official release. It may contain bugs, errors, or incomplete features that could affect its performance and functionality. By using this software, you agree to accept the risks and limitations associated with beta software. We appreciate your feedback and suggestions to help us improve this software, but we do not guarantee that we will implement them or that the software will meet your expectations. Please use this software at your own discretion and responsibility.

Key Features and Enhancements#

  • Decoder and Encoder can now restrict and reorder registered implementations with the decoder_ids and encoder_ids constructor arguments. Flat lists select implementations across codecs, while mappings define a complete per-codec allow-list. Existing backends restrictions take precedence and omit conflicting selections with a warning. See the plugin query and selection sample. The C nvimgcodecExecutionParams_t structure grew to carry this selection, so applications and extensions compiled against an earlier layout must be rebuilt for nvImageCodec 0.10. Legacy execution-parameter layouts from releases before 0.4 are no longer converted.

  • The C API can now enumerate registered extensions and retrieve the plugin framework descriptor. The framework descriptor lists registered codec names, including names known only through file-extension mappings, and executable decoder, encoder, and parser descriptions, with optional codec and backend filtering. Automatically discovered extensions can be unregistered with nvimgcodecExtensionDestroy. Python applications can inspect the registered identifiers with get_extension_ids(), get_decoder_ids(), get_encoder_ids(), and get_parser_ids(), and codec names with get_codec_names().

  • Planar RGB and BGR images (P_RGB / P_BGR) can now be encoded by GPU encoders that only accept interleaved samples, nvTIFF being the case that prompted this. When an encoder declines a sample because of its layout, the core now offers the interleaved equivalent and, if that is accepted, interleaves on the GPU before encoding. The resulting file is identical to the one produced from the equivalent interleaved input. Planar YUV (P_YUV) is not converted, and CPU-only encoders are unaffected.

  • Images can now be decoded to their native chroma subsampling instead of always being upsampled. Pass chroma_subsampling to DecodeParams to request, for example, CSS_420, and read the result through the new Image.planes, which returns one array view per plane so the chroma planes keep their reduced size. A chroma-subsampled Image can no longer be viewed as a single array (numpy.asarray, .shape, .strides, DLPack, the array interfaces); use Image.planes instead. Encoding a chroma-subsampled Image is not supported yet and raises ValueError. Without chroma_subsampling the decoded output is unchanged.

  • Reusable CodeStream JPEG 2000 encoding is about 5% faster after removing a redundant host wait before querying the encoded size.

  • nvTIFF now decodes compatible TIFF images and regions of interest from one decode call together, with native nvTIFF batching. The decoder groups the images and runs the groups in parallel automatically. The optional nvtiff_cuda_decoder:max_subbatch_images decoder option sets the maximum number of images in one native nvTIFF call. It does not limit total GPU memory.

  • The Linux shared libraries no longer leak internal C++ symbols. libnvimgcodec.so now exports only the documented nvimgcodec* C API, each extension exports only nvimgcodecExtensionModuleEntry, and the Python module exports only its PyInit_ entry point. The previously exported weak libstdc++ template instantiations were never part of the public interface; hiding them removes a source of symbol collisions when nvImageCodec is loaded alongside other libraries in the same process.

  • The installed CMake package now works when the install tree has been repackaged into a layout other than the plain cmake --install one – Debian multiarch, the RPM lib64 layout, and the Windows installer and redist layouts. The nvimgcodec_*_DIR variables and the imported target locations are resolved at find_package() time rather than baked in at build time. find_package(nvimgcodec) also no longer fails when only the shared library package is installed and the separately packaged static archive is absent.

  • find_package(nvimgcodec) now works without passing -Dnvimgcodec_DIR=. An unversioned dispatcher package config is installed to share/cmake/nvimgcodec – a location CMake searches on every install layout, including Debian multiarch – and forwards to the config matching the consumer’s CUDA major version, as determined by find_package(CUDAToolkit). When both CUDA 12 and CUDA 13 builds are installed, a project compiling against one toolkit no longer silently links the other; with no match, the highest installed major is used and reported.

  • Consumers linking the static library no longer need to link the CUDA driver themselves. nvimgcodec_static carries CUDA::cuda_driver on its INTERFACE, and the installed package config does a quiet find_package(CUDAToolkit) so the imported target exists when something links nvimgcodec_static; a consumer without the CUDA toolkit installed can still configure against the shared library. Previously a static-library consumer had to add the driver by hand.

  • Python cuda_stream arguments now accept objects implementing the CUDA Stream Protocol (__cuda_stream__) in addition to integer stream handles. None or omitting the argument means unspecified: handle 0 (the legacy default stream), unless wrapping a CUDA Array Interface source that advertises stream, in which case that stream is used. When both an explicit cuda_stream (including 0) and a CUDA Array Interface stream are present, the explicit argument takes precedence; previously the CUDA Array Interface value won. The caller is responsible for any ordering between the two. A protocol object is held by the resulting Image until that Image is released (so Image.cuda_stream and the CUDA Array Interface stream field stay valid); a bare integer handle is not retained.

  • as_image / as_images / from_dlpack now accept CPU tensors through the DLPack protocol, such as a PyTorch CPU tensor. Previously any DLPack source that was not CUDA-accessible was rejected with “Unsupported device in DLTensor”, even though the same data worked fine as a NumPy array. The resulting Image reports a host buffer, matching the existing NumPy as_image path.

  • Plugins can register file extensions for a codec and query the mappings through nvimgcodecFrameworkDesc_t. Encoder.encode with a leading-period codec such as ".jpg" and Encoder.write with a filename extension now resolve to the default registered codec. Input codec detection remains content-based: registered parsers inspect the bitstream and do not use the input filename extension. Python applications can list codec names and unique file extensions, with optional reciprocal filters, using get_codec_names() and get_file_extensions(), and manage additional mappings with register_file_extension() and unregister_file_extension(). Plugin and mapping registrations can be changed only while no decoder, encoder, or code stream from the instance is alive.

  • Breaking change: MAKE_SEMANTIC_VERSION has been replaced by NVIMGCODEC_MAKE_SEMANTIC_VERSION, which packs a version as major * 10000 + minor * 100 + patch. The previous encoding left a single digit for the minor component, so 0.10.0 was reported as 1.0.0. Packed values of released 0.x versions are unchanged, so existing version comparisons keep working; code using the old macro name has to be updated and recompiled. Minor and patch are now limited to 0-99.

  • Encoding an image that carries a non-identity orientation (a rotation or a mirror) is now rejected with an unsupported-orientation processing status, instead of the orientation being silently dropped and the image encoded unrotated. To keep the common round trip working, the Python Decoder now clears an image’s orientation once it has applied that orientation to the pixels (apply_exif_orientation=True, the default), so decoding an EXIF-oriented image and re-encoding it still succeeds. With apply_exif_orientation=False the decoded pixels are still in code stream orientation, so the orientation is kept and re-encoding that image is rejected. C API callers that build the decode output image, or the encode output code stream, from the nvimgcodecImageInfo_t returned by nvimgcodecCodeStreamGetImageInfo must reset orientation to identity after decoding and before encoding; the decoder itself does not change the output image’s information. The nvimtrans sample does this, so it keeps transcoding EXIF-oriented images, including with --ignore_orientation, where the EXIF orientation is dropped as before.

  • Added experimental support for Python 3.15 and free-threaded Python 3.15t. The Linux x86_64 and aarch64 wheels support both; the Windows wheels and the Tegra wheels (nvidia-nvimgcodec-tegra-cu12) support Python 3.15, but not the free-threaded 3.15t. Interoperability with libraries that do not publish Python 3.15 wheels yet, such as CuPy and PyTorch, has not been tested.

  • Updated nvJPEG2000 to v0.12.0, nvTIFF to v0.9.0 and nvCOMP to v6. The Python wheel extras now pin them to the minor version (nvCOMP: major).

  • Updated the CUDA Toolkit used to build the CUDA 13 release binaries from 13.3 to 13.4.

Fixed Issues#

  • JPEG 2000 GPU encoding now reports the correct output size when one code stream created with nvimgcodecCodeStreamCreateToHostMem is reused for several nvimgcodecEncoderEncode calls. Previously, if the resize callback returned pinned memory and a later image encoded to fewer bytes, the output kept the earlier, larger size. Encoding also now reports a failure, instead of success, when the encoded output cannot be written in full.

  • libtiff can now decode more than one image from the same TIFF source at the same time. Previously, the decodes used one shared read position, and could read data from the wrong location. This also applies when a fractional GPU load hint sends some images to the libtiff CPU fallback.

  • nvTIFF now completes decoding before it reports an image as ready. Previously, if the application released a decoded image immediately, the GPU could still use its output buffer or the encoded source data.

  • A decoder or encoder that fails to initialize (for example, the nvJPEG hardware decoder on a platform where nvjpegJpegStateCreate fails) is now skipped, and its images go to the next decoder or encoder for the codec. Previously it stayed first in line, and images assigned to it could be reported as successfully decoded although their output was never written.

  • When a batch decoder fails to decode some images, they now go straight to the next decoder for the codec that doesn’t decode in batches. Previously, a failed decoder that can also decode single images, such as the nvJPEG hardware decoder, first retried each of them one at a time, and debug builds aborted.

  • Images that a batch decoder fails to decode are now decoded only once, by the next decoder. Previously, when the decoder used more than one CPU thread (the default), another thread could pick them up while they were being handed over, so two threads decoded the same image at the same time, which could crash the process.

  • nvimgcodecDecoderCanDecode and nvimgcodecEncoderCanEncode now work with pre_init enabled. Previously they reported NVIMGCODEC_PROCESSING_STATUS_CODEC_UNSUPPORTED on the first call and, on later calls, answered based on the images from the previous decode or encode call.

  • nvimgcodecDecoderCanDecode and nvimgcodecEncoderCanEncode no longer crash when called right after the results of a decode or encode call on the same decoder or encoder become available. Previously they could change internal state that the worker threads of that call were still using.

  • With a custom executor, decoders and encoders no longer call its wait before its run, which the executor documentation does not allow. When wait reports an error, the decode or encode call now fails instead of continuing while tasks of the previous call may still be running.

  • A GPU batch decoder with a CPU fallback and a full load hint (1.0) no longer limits later batches to the size of the first batch. Previously, if the first decode call had one image, later batches sent most of their images to the CPU fallback.

  • TIFF images that specify bit depth or sample format for fewer channels than they contain (common among GeoTIFFs) now apply that value to every channel. Previously extra channels could be reported with the wrong type — for example a signed 16-bit RGB image as one 16-bit signed plane and two 8-bit unsigned planes.

  • TIFF images with signed integer samples can now be decoded to an unsigned output on the GPU. nvTIFF’s RGB output formats accept unsigned input only, so a signed source combined with an RGB or BGR request was refused by nvtiffDecodeCheckSupported and the sample was rejected without a message at the default log level. Such images are now decoded in their native format and converted on the GPU, which is what a grayscale request already did. Signed values are mapped onto the unsigned range with the usual midpoint shift, so the most negative input becomes 0, zero becomes mid-scale and the most positive input becomes the type’s maximum.

  • TIFF images with signed integer samples (8, 16 or 32-bit) can now be decoded on the CPU. The libTIFF extension previously rejected such images with “Unsupported sample format”. Palette images are unaffected, since a palette index is not itself a color sample.

  • Fixed a bug in the CPU TIFF decoder that mis-ordered the bytes of 16-bit palette indices, producing wrong colors.

  • Converting image data to or from 32-bit float on the GPU is now correct. Decoding 12-bit JPEG 2000 or TIFF to 32-bit float now maps the full input range onto [0, 1] (for example, 4095 maps to 1.0). Those samples were previously scaled as if they were 31-bit integers, producing values about 5.2e5 times too large, and debug builds aborted on an assertion instead of producing them.

  • Fixed a cleanup crash when a non-default CUDA stream passed to Decoder.read or Decoder.decode was destroyed before the decoded output. Decoder-created and reused output buffers no longer retain the caller-owned stream in their deleters.

  • Using a CodeStream or Image created on one nvimgcodecInstance with a Decoder or Encoder from another instance now returns NVIMGCODEC_STATUS_INVALID_PARAMETER instead of causing a crash.

  • Fixed a process abort when parsing JPEG files with an APP1 segment too short to hold an EXIF identifier. Such segments are now skipped instead of being read past their declared end, so malformed streams are reported as a bad codestream instead of crashing.

  • WebP files whose EXIF chunk size exceeds the remaining bitstream are now rejected with NVIMGCODEC_STATUS_BAD_CODESTREAM instead of attempting a multi-gigabyte allocation.

  • WebP VP8, VP8L, and VP8X chunks whose declared size is smaller than the fixed header fields they require are now rejected with NVIMGCODEC_STATUS_BAD_CODESTREAM.

  • WebP VP8X canvases whose width times height exceeds 2^32 - 1 are now rejected with NVIMGCODEC_STATUS_BAD_CODESTREAM.

  • JPEG 2000 files containing an extended-length box — one that sets its 32-bit LBox field to 1 so that the actual length is carried by the 64-bit XLBox field that follows — are now read instead of being rejected with Invalid box size: 1.

  • Malformed JPEG 2000 codestreams with invalid or inconsistent image and tile geometry (zero or out-of-range tile dimensions, a tile grid that does not cover the image, an invalid component count, or a header whose declared size does not match the component count) are now rejected instead of potentially crashing or reporting bad metadata when the image or tile geometry is queried.

  • Corrected the reported JPEG 2000 tile count (CodeStream.num_tiles_x / CodeStream.num_tiles_y) for images whose canvas origin is not at (0, 0). Such images were previously undercounted, which could cause callers iterating over tiles to miss the last row or column.

  • Fixed a segmentation fault in the conda packages when calling CodeStream.get_sub_code_stream() with a CodeStreamView. pybind11 3.1.0 mishandles the internal “try next overload” sentinel for overloaded functions that use a keep_alive return-value policy (pybind/pybind11#6154); the binding no longer relies on that policy for this function.

  • Fixed a potential crash when a Decoder or Encoder is destroyed near process exit (for example, as part of interpreter shutdown alongside other native extensions). Teardown of the underlying CUDA/nvJPEG/nvJPEG2000/nvTIFF resources is now skipped once the process is known to be exiting, since the CUDA driver or the codec library may already be unavailable at that point.

  • Fixed 16-bit encoding in the nvpnm example encoder, which produced incorrect files: uint16 samples were truncated and only the first half of each row was written. The encoder now writes the full samples.

  • The nvpnm example encoder now rejects images whose planes do not all cover the first plane’s rectangle - a differing per-plane width or height, or a row_stride shorter than one row - with NVIMGCODEC_PROCESSING_STATUS_RESOLUTION_UNSUPPORTED. Such images were previously encoded by reading past the end of the input buffer.

  • Hardened the C API against invalid caller input: NULL entries in a decode/encode batch array, an out-of-range num_planes, and chained structures with a mismatched struct_size now return NVIMGCODEC_STATUS_INVALID_PARAMETER instead of crashing or aborting debug builds.

  • An nvimgcodecExecutionParams_t whose struct_size does not match the installed header is now rejected with NVIMGCODEC_STATUS_INTERNAL_ERROR before any of its other fields are read.

  • Malformed BMP, PNM, PNG and JPEG 2000 files are now rejected with NVIMGCODEC_STATUS_BAD_CODESTREAM instead of aborting debug builds or driving huge allocations.

  • Fixed out-of-bounds accesses in the nvJPEG, libtiff and libjpeg-turbo decoders on crafted input.

  • Exceptions thrown inside codec extensions and parsers are now always caught at the C boundary; some types could previously escape a plugin callback and leave a batch item without a reported status.

  • PBM (P1/P4) headers are no longer misparsed as carrying a maximum sample value.

  • Decoder.get_metadata with a kind argument now returns that kind’s metadata. For an image carrying more than one kind, asking for anything other than the first one returned the wrong kind’s data - for example requesting the ICC profile of an Aperio TIFF returned the Aperio description instead, reported under the wrong kind and with a mismatched value count. Metadata is now matched by the kind that was asked for rather than by its position in the image.

  • Several TIFF tags can now be requested in a single metadata query. Asking for more than one tag at a time previously aborted debug builds.

  • GPU decoding of tiled JPEG 2000 images with many tiles is significantly faster. Each tile’s CUDA stream was being made to wait on the previous tile’s completion before starting, serializing what is meant to be a pool of concurrently-decoding tile streams down to effectively one tile at a time. A 5000x6400 image tiled at 256x256 (500 tiles) decoded roughly 6x faster after the fix on the hardware used to measure it; the effect scales with tile count.

  • Building from source with CUDA_TARGET_ARCHS set to values in standard CMake CUDA_ARCHITECTURES syntax (for example 90a-real or 121-virtual, as conda-forge’s build configuration provides) no longer fails with “None of the provided CUDA architectures is supported”. Such values were previously rejected outright because the compiler-support check did not recognize the -real/-virtual suffix.

  • The DICOM HTJ2K transcoding and multi-frame conversion tools no longer turn malformed study, series, acquisition, or content dates and times into a different value (NVIDIA/nvImageCodec#56). Values that are not a valid DICOM date (YYYYMMDD) or time (HH, HHMM, or HHMMSS with up to 6 fractional digits, or the legacy colon-separated form) are now removed. Previously, for example, a time of 12345 was stored as 12:34:00 and a date of 202001019 as 2020-01-01.

  • Lossless WebP encoding of images with an alpha channel is now exact. RGB values at fully-transparent (alpha == 0) pixels were previously altered by the encode/decode round trip, unlike 3-channel images and every other lossless codec. This is fixed by bumping the bundled OpenCV dependency to 4.14.0 and requesting its new exact-preservation lossless mode for 4-channel WebP output.

Known Issues#

  • CUDA 12 is not supported on RHEL 10, SLES 16 / openSUSE 16, Ubuntu 26.04, Debian 13, Fedora 44, Kylin 11, and Debian 12 on sbsa. Due to a packaging issue, CUDA 12 installers are provided for these distributions. They are not supported and can be ignored.

Deprecated and removed features#

  • Support for Python 3.9 is deprecated and will be removed in a future release. Python 3.9 reached end of life at the end of 2025. Wheels are still built for it and python_requires still starts at >=3.9, but importing nvidia.nvimgcodec on Python 3.9 now emits an NvImageCodecDeprecatedPythonWarning. Please upgrade to Python 3.10 or newer.

  • Support for free-threaded Python 3.13t has been removed. The Linux wheels no longer include a module for it, so import nvidia.nvimgcodec fails on Python 3.13t even though pip still installs the wheels there. Use free-threaded Python 3.14t instead.