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#
DecoderandEncodercan now restrict and reorder registered implementations with thedecoder_idsandencoder_idsconstructor arguments. Flat lists select implementations across codecs, while mappings define a complete per-codec allow-list. Existingbackendsrestrictions take precedence and omit conflicting selections with a warning. See the plugin query and selection sample. The CnvimgcodecExecutionParams_tstructure 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 withget_extension_ids(),get_decoder_ids(),get_encoder_ids(), andget_parser_ids(), and codec names withget_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_subsamplingtoDecodeParamsto request, for example,CSS_420, and read the result through the newImage.planes, which returns one array view per plane so the chroma planes keep their reduced size. A chroma-subsampledImagecan no longer be viewed as a single array (numpy.asarray,.shape,.strides, DLPack, the array interfaces); useImage.planesinstead. Encoding a chroma-subsampledImageis not supported yet and raisesValueError. Withoutchroma_subsamplingthe decoded output is unchanged.Reusable
CodeStreamJPEG 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
decodecall together, with native nvTIFF batching. The decoder groups the images and runs the groups in parallel automatically. The optionalnvtiff_cuda_decoder:max_subbatch_imagesdecoder 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.sonow exports only the documentednvimgcodec*C API, each extension exports onlynvimgcodecExtensionModuleEntry, and the Python module exports only itsPyInit_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 --installone – Debian multiarch, the RPMlib64layout, and the Windows installer and redist layouts. Thenvimgcodec_*_DIRvariables and the imported target locations are resolved atfind_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 toshare/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 byfind_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_staticcarriesCUDA::cuda_driveron itsINTERFACE, and the installed package config does a quietfind_package(CUDAToolkit)so the imported target exists when something linksnvimgcodec_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_streamarguments now accept objects implementing the CUDA Stream Protocol (__cuda_stream__) in addition to integer stream handles.Noneor omitting the argument means unspecified: handle0(the legacy default stream), unless wrapping a CUDA Array Interface source that advertisesstream, in which case that stream is used. When both an explicitcuda_stream(including0) and a CUDA Array Interfacestreamare 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 resultingImageuntil thatImageis released (soImage.cuda_streamand the CUDA Array Interfacestreamfield stay valid); a bare integer handle is not retained.as_image/as_images/from_dlpacknow 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 resultingImagereports a host buffer, matching the existing NumPyas_imagepath.Plugins can register file extensions for a codec and query the mappings through
nvimgcodecFrameworkDesc_t.Encoder.encodewith a leading-period codec such as".jpg"andEncoder.writewith 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, usingget_codec_names()andget_file_extensions(), and manage additional mappings withregister_file_extension()andunregister_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_VERSIONhas been replaced byNVIMGCODEC_MAKE_SEMANTIC_VERSION, which packs a version asmajor * 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
Decodernow 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. Withapply_exif_orientation=Falsethe 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 thenvimgcodecImageInfo_treturned bynvimgcodecCodeStreamGetImageInfomust resetorientationto identity after decoding and before encoding; the decoder itself does not change the output image’s information. Thenvimtranssample 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
nvimgcodecCodeStreamCreateToHostMemis reused for severalnvimgcodecEncoderEncodecalls. 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
nvjpegJpegStateCreatefails) 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.
nvimgcodecDecoderCanDecodeandnvimgcodecEncoderCanEncodenow work withpre_initenabled. Previously they reportedNVIMGCODEC_PROCESSING_STATUS_CODEC_UNSUPPORTEDon the first call and, on later calls, answered based on the images from the previousdecodeorencodecall.nvimgcodecDecoderCanDecodeandnvimgcodecEncoderCanEncodeno longer crash when called right after the results of adecodeorencodecall 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
waitbefore itsrun, which the executor documentation does not allow. Whenwaitreports an error, thedecodeorencodecall 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 firstdecodecall 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
nvtiffDecodeCheckSupportedand 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 to1.0). Those samples were previously scaled as if they were 31-bit integers, producing values about5.2e5times 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.readorDecoder.decodewas destroyed before the decoded output. Decoder-created and reused output buffers no longer retain the caller-owned stream in their deleters.Using a
CodeStreamorImagecreated on onenvimgcodecInstancewith aDecoderorEncoderfrom another instance now returnsNVIMGCODEC_STATUS_INVALID_PARAMETERinstead 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_CODESTREAMinstead of attempting a multi-gigabyte allocation.WebP
VP8,VP8L, andVP8Xchunks whose declared size is smaller than the fixed header fields they require are now rejected withNVIMGCODEC_STATUS_BAD_CODESTREAM.WebP
VP8Xcanvases whose width times height exceeds2^32 - 1are now rejected withNVIMGCODEC_STATUS_BAD_CODESTREAM.JPEG 2000 files containing an extended-length box — one that sets its 32-bit
LBoxfield to 1 so that the actual length is carried by the 64-bitXLBoxfield that follows — are now read instead of being rejected withInvalid 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 aCodeStreamView. pybind11 3.1.0 mishandles the internal “try next overload” sentinel for overloaded functions that use akeep_alivereturn-value policy (pybind/pybind11#6154); the binding no longer relies on that policy for this function.Fixed a potential crash when a
DecoderorEncoderis 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
nvpnmexample encoder, which produced incorrect files:uint16samples were truncated and only the first half of each row was written. The encoder now writes the full samples.The
nvpnmexample encoder now rejects images whose planes do not all cover the first plane’s rectangle - a differing per-plane width or height, or arow_strideshorter than one row - withNVIMGCODEC_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 mismatchedstruct_sizenow returnNVIMGCODEC_STATUS_INVALID_PARAMETERinstead of crashing or aborting debug builds.An
nvimgcodecExecutionParams_twhosestruct_sizedoes not match the installed header is now rejected withNVIMGCODEC_STATUS_INTERNAL_ERRORbefore any of its other fields are read.Malformed BMP, PNM, PNG and JPEG 2000 files are now rejected with
NVIMGCODEC_STATUS_BAD_CODESTREAMinstead 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_metadatawith akindargument 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 wrongkindand 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_ARCHSset to values in standard CMakeCUDA_ARCHITECTURESsyntax (for example90a-realor121-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/-virtualsuffix.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, orHHMMSSwith up to 6 fractional digits, or the legacy colon-separated form) are now removed. Previously, for example, a time of12345was stored as 12:34:00 and a date of202001019as 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_requiresstill starts at>=3.9, but importingnvidia.nvimgcodecon Python 3.9 now emits anNvImageCodecDeprecatedPythonWarning. 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.nvimgcodecfails on Python 3.13t even though pip still installs the wheels there. Use free-threaded Python 3.14t instead.