Skip to content

pixtreme 2.0.0

Latest

Choose a tag to compare

@minamikik minamikik released this 25 Oct 07:49
· 1 commit to main since this release

Wheel validation included full test suite runs on RTX A6000 (compute capability 8.6), RTX 6000 Ada Generation (8.9),
and GeForce RTX 5090 (12.0). NVIDIA driver R580 and compute capability 7.5 (Turing) have not been verified on hardware.

Added

  • Added a public C and C++ SDK: headers, libpixtreme.so.2, and a CMake package are included in the Linux wheel.
    C11 and C++17 applications can use pixtreme without embedding Python. Locate the package with
    pixtreme.core.get_cmake_dir(), use find_package(pixtreme CONFIG REQUIRED), and link pixtreme::pixtreme.
    Python and native consumers share the same library; keep pixtreme installed rather than copying its library.
  • The SDK provides GPU Frames with color metadata, channel parsing, borrowed pixel owners, transactional pixel
    replacement, shallow or deep cloning, and DLPack 1.x exchange. Operations accept a CUDA stream and an optional
    allocator for output and scratch memory; the default allocator uses CUDA stream-ordered memory allocation.
  • The SDK exposes token matching and canonical token constants, colorspace and reference-white definitions,
    and Lut / Lut1D validation. It also provides cast_dtype, recode_dtype, quantize, dequantize,
    full_to_legal, legal_to_full, and channel shuffle. Native shuffle requires matching colorspace and gamma;
    Python's shuffle(adapt=True) retains its color-conversion behavior.
  • C and C++ callers can read EXR headers and pixels, write EXR images, and write mixed HALF, FLOAT, and UINT
    channel groups without Python. Supported EXR options, pixel types, and color metadata follow the Python EXR APIs.
  • The shared library has a C ABI with opaque handles and size-prefixed structs, plus a header-only C++ wrapper.
    Within a major version, the ABI grows through added functions and trailing struct fields; programs built with
    earlier minor headers run against later minor libraries without rebuilding. Use pixtreme_check_abi or
    pixtreme::require_abi() to check the loaded library against the headers used to build the application.
  • Wheels include full license texts for bundled third-party components in dist-info/licenses/ and identify them
    in the SPDX License-Expression. A separate notice identifies the statically linked CUDA runtime's license.
  • GitHub Releases include an HTML documentation archive containing the API reference and the C and C++ SDK guide.

Changed

  • pixtreme.core.Frame now wraps one persistent C++ Frame. It no longer inherits
    pydantic.BaseModel and no longer provides model_fields, model_dump,
    model_construct, or model_copy. vars(frame) and frame.__dict__ are
    unavailable, and fields cannot be deleted with del. Read and assign the five
    properties instead. Validation errors are ValueError rather
    than pydantic.ValidationError. For migration, read the five fields directly,
    use the keyword-only constructor to build a Frame, and use copy.copy or
    copy.deepcopy to copy one. Field names and order, defaults, validation on
    assignment, contiguous-array identity, equality, unhashability, and pickle
    roundtrips are preserved. pixtreme.io.ImageHeader remains a pydantic model.
  • The Python source, tests, project metadata, and lock file now live in python/; native C++ implementations follow
    the corresponding Python package paths under cpp/. The build backend is scikit-build-core with setuptools-scm
    versions from vX.Y.Z tags and a nanobind extension. Existing Python image-processing operation names and
    arguments remain unchanged.
  • 2.0.0 wheels target Linux x86_64 (manylinux_2_28) with the cp312-abi3 tag for standard CPython 3.12 and newer.
    Free-threaded CPython builds are not supported. 2.0.0 does not provide native Windows wheels. On Windows, use the
    Linux wheel under WSL2, or use 1.x for native Windows. Installing and using the wheel requires no compiler or
    CUDA toolkit; CUDA's runtime is linked statically. PyPI and GitHub Releases distribute wheels only; source
    distributions are not published.
  • EXR reading and writing now use the shared C++ library, including container parsing, part and channel selection,
    and all ten compression modes: NONE, RLE, ZIPS, ZIP, PIZ, PXR24, B44, B44A, DWAA, and DWAB. Native implementations
    use C++ and CUDA. ZIP, ZIPS, and PXR24 use libdeflate for Deflate compression and decompression, and the
    nvidia-nvcomp-cu13 runtime dependency has been removed.
  • Floating-point results follow each operation's specified precision. Bit-for-bit equality is not guaranteed
    across GPU generations, CPU instruction sets, or math libraries.

Fixed

  • Fixed reading PXR24 EXR images with mixed HALF, FLOAT, and UINT channels into a float32 Frame, including
    scanline, tiled, and multipart files.
  • Corrected B44 and B44A pLinear HALF decoding to match OpenEXR for external EXR files, including the
    0x1935 pattern decoding to 0xd1f9.
  • For EXR files without chromaticities, read_header(...).color.raw now retains an explicitly stored
    acesImageContainerFlag=0 as {'acesImageContainerFlag': False}.
  • white_point_simulation rejects reference whites on or within float64 roundoff of an edge of the Frame
    primaries' chromaticity triangle with a ValueError explaining the reason and correction. This also applies
    when the input and output reference whites are equal.
  • Frame cleanup releases pixels without unraisable exceptions when the active Python frame's builtins omit
    __import__, including garbage collection inside collections.namedtuple constructors.
  • Keeping Frames alive until Python interpreter shutdown no longer emits cleanup errors to stderr or interferes
    with process exit.

Migration

Frame uses five properties: data, colorspace, gamma, channels, and
matrix. Replace pydantic model operations with field access and the validated
keyword-only constructor.

# Before
fields = frame.model_dump()
frame = px.core.Frame.model_construct(
    data=data, colorspace="ACEScg", gamma="linear", channels="RGB", matrix=None
)

# 2.0
fields = {name: getattr(frame, name) for name in ("data", "colorspace", "gamma", "channels", "matrix")}
frame = px.core.Frame(
    data=data, colorspace="ACEScg", gamma="linear", channels="RGB", matrix=None
)

Use the standard library's copy operations. A shallow copy shares pixels, a deep
copy copies pixels, and both copies have independent metadata.

# Before
shallow = frame.model_copy()
deep = frame.model_copy(deep=True)

# 2.0
import copy

shallow = copy.copy(frame)
deep = copy.deepcopy(frame)

Catch ValueError when constructing a Frame or assigning a field:

# Before
try:
    frame.gamma = gamma
except pydantic.ValidationError as error:
    print(error)

# 2.0
try:
    frame.gamma = gamma
except ValueError as error:
    print(error)

vars(frame) and frame.__dict__ are unavailable; read the five properties or
create a field dictionary as above. Mutating such a dictionary does not assign
Frame properties. Fields cannot be removed with del frame.colorspace or another
del frame.<field> operation. Assign valid values through the properties.