Python bindings for a fast RLE decoder/encoder, with a focus on use as a plugin for pylibjpeg
Project Links
Meta
Author: pylibjpeg-rle contributors
Requires Python: >=3.10
Classifiers
License
- OSI Approved :: MIT License
Intended Audience
- Developers
- Healthcare Industry
- Science/Research
Development Status
- 5 - Production/Stable
Natural Language
- English
Programming Language
- Rust
- Python :: 3.10
- Python :: 3.11
- Python :: 3.12
- Python :: 3.13
- Python :: 3.14
Operating System
- MacOS :: MacOS X
- POSIX :: Linux
- Microsoft :: Windows
Topic
- Scientific/Engineering :: Medical Science Apps.
- Software Development :: Libraries
pylibjpeg-rle
A fast DICOM (PackBits) RLE plugin for pylibjpeg, written in Rust with a Python wrapper.
Linux, MacOS and Windows are all supported.
Installation
Installing the current release
pip install pylibjpeg-rle
Installing the development version
Make sure Python, Git and Rust are installed. For Windows, you also need to install Microsoft's C++ Build Tools.
git clone https://github.com/pydicom/pylibjpeg-rle
cd pylibjpeg-rle
python -m pip install .
Supported Transfer Syntaxes
UID | Description | Decoding | Encoding |
---|---|---|---|
1.2.840.10008.1.2.5 | RLE Lossless | Yes | Yes |
Usage
Decoding
With pylibjpeg
from pydicom import dcmread
from pydicom.data import get_testdata_file
ds = dcmread(get_testdata_file("OBXXXX1A_rle.dcm"))
arr = ds.pixel_array
Standalone with pydicom
Alternatively you can use the included functions to decode a given dataset:
from rle import pixel_array, generate_frames
# Return the entire Pixel Data as an ndarray
arr = pixel_array(ds)
# Generator function that only processes 1 frame at a time,
# may help reduce memory usage when dealing with large Pixel Data
for arr in generate_frames(ds):
print(arr.shape)
Encoding
Standalone with pydicom
Convert uncompressed pixel data to RLE encoding and save:
from pydicom import dcmread
from pydicom.data import get_testdata_file
from pydicom.uid import RLELossless
from rle import pixel_data
# Get the uncompressed pixel data
ds = dcmread(get_testdata_file("OBXXXX1A.dcm"))
arr = ds.pixel_array
# RLE encode and encapsulate `arr`
ds.PixelData = pixel_data(arr, ds)
# Set the correct *Transfer Syntax UID*
ds.file_meta.TransferSyntaxUID = RLELossless
ds.save_as('as_rle.dcm')
Benchmarks
Decoding
Time per 1000 decodes, pydicom's default RLE decoder vs. pylibjpeg-rle:
Dataset | Pixels | Bytes | pydicom | pylibjpeg-rle |
---|---|---|---|---|
OBXXXX1A_rle.dcm | 480,000 | 480,000 | 5.7 s | 1.1 s |
OBXXXX1A_rle_2frame.dcm | 960,000 | 960,000 | 11.5 s | 2.1 s |
SC_rgb_rle.dcm | 10,000 | 30,000 | 0.28 s | 0.19 s |
SC_rgb_rle_2frame.dcm | 20,000 | 60,000 | 0.45 s | 0.28 s |
MR_small_RLE.dcm | 4,096 | 8,192 | 0.46 s | 0.15 s |
emri_small_RLE.dcm | 40,960 | 81,920 | 1.8 s | 0.67 s |
SC_rgb_rle_16bit.dcm | 10,000 | 60,000 | 0.48 s | 0.25 s |
SC_rgb_rle_16bit_2frame.dcm | 20,000 | 120,000 | 0.86 s | 0.39 s |
rtdose_rle_1frame.dcm | 100 | 400 | 0.16 s | 0.13 s |
rtdose_rle.dcm | 1,500 | 6,000 | 1.0 s | 0.64 s |
SC_rgb_rle_32bit.dcm | 10,000 | 120,000 | 0.82 s | 0.35 s |
SC_rgb_rle_32bit_2frame.dcm | 20,000 | 240,000 | 1.5 s | 0.60 s |
Encoding
Time per 1000 encodes, pydicom's default RLE encoder vs. pylibjpeg-rle and python-gdcm:
Dataset | Pixels | Bytes | pydicom | pylibjpeg-rle | python-gdcm |
---|---|---|---|---|---|
OBXXXX1A.dcm | 480,000 | 480,000 | 30.6 s | 1.4 s | 1.5 s |
SC_rgb.dcm | 10,000 | 30,000 | 1.9 s | 0.11 s | 0.21 s |
MR_small.dcm | 4,096 | 8,192 | 3.0 s | 0.11 s | 0.29 s |
SC_rgb_16bit.dcm | 10,000 | 60,000 | 3.6 s | 0.18 s | 0.28 s |
rtdose_1frame.dcm | 100 | 400 | 0.28 s | 0.04 s | 0.14 s |
SC_rgb_32bit.dcm | 10,000 | 120,000 | 7.1 s | 0.32 s | 0.43 s |
Sep 07, 2025
2.2.0
May 01, 2025
2.1.0
Jan 06, 2024
2.0.0
Jan 22, 2022
1.3.0
Jan 02, 2022
1.2.0
May 02, 2021
1.1.0
Apr 23, 2021
1.0.0