Skill Tracer
Skills › Scientific Agent Skills › pydicom › transfer_syntaxes.md

transfer_syntaxes.md

384 lines · skills/pydicom/references/transfer_syntaxes.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Transfer syntaxes, pixel plugins, and encapsulation
2
3Transfer Syntax UID `(0002,0010)` identifies the encoding rules for the
4dataset, including VR encoding, byte order, and pixel compression. This guide
5targets stable pydicom 3.0.2. Always use the applicable DICOM PS3.5/PS3.6 and
6the deployment's conformance statements for interoperability decisions. Snippets
7with file paths, `encoded_frames`, or callback placeholders are illustrative;
8repository tests exercise these APIs with small synthetic data.
9

Transfer syntaxes, pixel plugins, and encapsulation

Transfer Syntax UID (0002,0010) identifies the encoding rules for the dataset, including VR encoding, byte order, and pixel compression. This guide targets stable pydicom 3.0.2. Always use the applicable DICOM PS3.5/PS3.6 and the deployment's conformance statements for interoperability decisions. Snippets with file paths, encoded_frames, or callback placeholders are illustrative; repository tests exercise these APIs with small synthetic data.

Raw lines 1 to 9
10## Inspect before decoding
12```python
13from pydicom import dcmread
15ds = dcmread(
16 "authorized/image.dcm",
17 stop_before_pixels=True,
18 specific_tags=[
19 "Rows",
20 "Columns",
21 "NumberOfFrames",
22 "SamplesPerPixel",
23 "BitsAllocated",
24 "BitsStored",
25 "PhotometricInterpretation",
26 ],
28ts = ds.file_meta.TransferSyntaxUID
29technical = {
30 "uid": str(ts),
31 "name": ts.name,
32 "compressed": ts.is_compressed,
33 "implicit_vr": ts.is_implicit_VR,
34 "little_endian": ts.is_little_endian,
36```
38Do not infer decoder support from the UID name. Run:
40```bash
41python scripts/transfer_syntax_inspector.py --input authorized/image.dcm
42python scripts/pixel_frame_planner.py authorized/image.dcm --frames 0
43```
45Plugin availability is not proof that a particular codestream, bit depth,
46color representation, or platform is handled correctly.

Inspect before decoding

from pydicom import dcmread

ds = dcmread(
    "authorized/image.dcm",
    stop_before_pixels=True,
    specific_tags=[
        "Rows",
        "Columns",
        "NumberOfFrames",
        "SamplesPerPixel",
        "BitsAllocated",
        "BitsStored",
        "PhotometricInterpretation",
    ],
)
ts = ds.file_meta.TransferSyntaxUID
technical = {
    "uid": str(ts),
    "name": ts.name,
    "compressed": ts.is_compressed,
    "implicit_vr": ts.is_implicit_VR,
    "little_endian": ts.is_little_endian,
}

Do not infer decoder support from the UID name. Run:

python scripts/transfer_syntax_inspector.py --input authorized/image.dcm
python scripts/pixel_frame_planner.py authorized/image.dcm --frames 0

Plugin availability is not proof that a particular codestream, bit depth, color representation, or platform is handled correctly.

Raw lines 10 to 47
Runs code python scripts/transfer_syntax_inspector.py :41
Runs code python scripts/pixel_frame_planner.py :42
48## Native and dataset-compressed transfer syntaxes
50| Name | UID | Encoding | pydicom constant |
51|---|---|---|---|
52| Implicit VR Little Endian | 1.2.840.10008.1.2 | implicit VR, little endian | `ImplicitVRLittleEndian` |
53| Explicit VR Little Endian | 1.2.840.10008.1.2.1 | explicit VR, little endian | `ExplicitVRLittleEndian` |
54| Deflated Explicit VR Little Endian | 1.2.840.10008.1.2.1.99 | deflated dataset | `DeflatedExplicitVRLittleEndian` |
55| Explicit VR Big Endian | 1.2.840.10008.1.2.2 | explicit VR, big endian; retired | `ExplicitVRBigEndian` |
57Explicit VR Big Endian was retired in 2006 and should not be selected for new
58objects. pydicom can read it, but endianness conversion when writing is not an
59automatic `Dataset.save_as()` operation.
61The default DICOM network Transfer Syntax is Implicit VR Little Endian. This is
62not a recommendation to omit File Meta Information from files.

Native and dataset-compressed transfer syntaxes

NameUIDEncodingpydicom constant
Implicit VR Little Endian1.2.840.10008.1.2implicit VR, little endianImplicitVRLittleEndian
Explicit VR Little Endian1.2.840.10008.1.2.1explicit VR, little endianExplicitVRLittleEndian
Deflated Explicit VR Little Endian1.2.840.10008.1.2.1.99deflated datasetDeflatedExplicitVRLittleEndian
Explicit VR Big Endian1.2.840.10008.1.2.2explicit VR, big endian; retiredExplicitVRBigEndian

Explicit VR Big Endian was retired in 2006 and should not be selected for new objects. pydicom can read it, but endianness conversion when writing is not an automatic Dataset.save_as() operation.

The default DICOM network Transfer Syntax is Implicit VR Little Endian. This is not a recommendation to omit File Meta Information from files.

Raw lines 48 to 63
64## Encapsulated image transfer syntaxes
66| Family | Name | UID | Loss |
67|---|---|---|---|
68| JPEG | JPEG Baseline 8-bit | 1.2.840.10008.1.2.4.50 | lossy |
69| JPEG | JPEG Extended 12-bit | 1.2.840.10008.1.2.4.51 | lossy |
70| JPEG | JPEG Lossless Process 14 | 1.2.840.10008.1.2.4.57 | lossless |
71| JPEG | JPEG Lossless Process 14 SV1 | 1.2.840.10008.1.2.4.70 | lossless |
72| JPEG-LS | JPEG-LS Lossless | 1.2.840.10008.1.2.4.80 | lossless |
73| JPEG-LS | JPEG-LS Near-Lossless | 1.2.840.10008.1.2.4.81 | near-lossless |
74| JPEG 2000 | JPEG 2000 Lossless Only | 1.2.840.10008.1.2.4.90 | lossless |
75| JPEG 2000 | JPEG 2000 | 1.2.840.10008.1.2.4.91 | lossless or lossy in DICOM; pydicom encoding treats it as lossy |
76| HTJ2K | HTJ2K Lossless | 1.2.840.10008.1.2.4.201 | lossless |
77| HTJ2K | HTJ2K RPCL Lossless | 1.2.840.10008.1.2.4.202 | lossless |
78| HTJ2K | HTJ2K | 1.2.840.10008.1.2.4.203 | lossy/lossless by syntax rules |
79| RLE | RLE Lossless | 1.2.840.10008.1.2.5 | lossless |
81In pydicom 3.0, `JPEGLossless` is `.57`; use `JPEGLosslessSV1` for `.70`.
83Video, JPIP-referenced, encapsulated uncompressed, JPEG XL, and other current
84DICOM transfer syntaxes exist but are not all decoded by pydicom's pixel API.
85Consult PS3.6 and the installed `get_decoder()` result instead of assuming that
86all registered UIDs are supported.

Encapsulated image transfer syntaxes

FamilyNameUIDLoss
JPEGJPEG Baseline 8-bit1.2.840.10008.1.2.4.50lossy
JPEGJPEG Extended 12-bit1.2.840.10008.1.2.4.51lossy
JPEGJPEG Lossless Process 141.2.840.10008.1.2.4.57lossless
JPEGJPEG Lossless Process 14 SV11.2.840.10008.1.2.4.70lossless
JPEG-LSJPEG-LS Lossless1.2.840.10008.1.2.4.80lossless
JPEG-LSJPEG-LS Near-Lossless1.2.840.10008.1.2.4.81near-lossless
JPEG 2000JPEG 2000 Lossless Only1.2.840.10008.1.2.4.90lossless
JPEG 2000JPEG 20001.2.840.10008.1.2.4.91lossless or lossy in DICOM; pydicom encoding treats it as lossy
HTJ2KHTJ2K Lossless1.2.840.10008.1.2.4.201lossless
HTJ2KHTJ2K RPCL Lossless1.2.840.10008.1.2.4.202lossless
HTJ2KHTJ2K1.2.840.10008.1.2.4.203lossy/lossless by syntax rules
RLERLE Lossless1.2.840.10008.1.2.5lossless

In pydicom 3.0, JPEGLossless is .57; use JPEGLosslessSV1 for .70.

Video, JPIP-referenced, encapsulated uncompressed, JPEG XL, and other current DICOM transfer syntaxes exist but are not all decoded by pydicom's pixel API. Consult PS3.6 and the installed get_decoder() result instead of assuming that all registered UIDs are supported.

Raw lines 64 to 87
88## Stable 3.0.2 decompression plugins
90The pydicom 3.0.2 `pixels` backend and plugin source expose these main choices
91(the older general handler guide includes legacy backend entries):
93| Transfer-syntax family | Typical pydicom plugin dependencies |
94|---|---|
95| Native/deflated | pydicom + NumPy |
96| RLE Lossless | built-in pydicom; `pylibjpeg-rle` |
97| JPEG Baseline/Extended | `pylibjpeg-libjpeg`; GDCM; Pillow with JPEG support |
98| JPEG Lossless | `pylibjpeg-libjpeg`; GDCM |
99| JPEG-LS | `pyjpegls`; `pylibjpeg-libjpeg`; GDCM |
100| JPEG 2000 | `pylibjpeg-openjpeg`; GDCM; Pillow with OpenJPEG |
101| HTJ2K | `pylibjpeg-openjpeg` |
103Pinned reviewed installations (Python 3.12+ for NumPy 2.5.3):
105```bash
106uv pip install "pydicom==3.0.2" "numpy==2.5.3"
108uv pip install "pylibjpeg==2.1.0" \
109 "pylibjpeg-libjpeg==2.4.0" \
110 "pylibjpeg-openjpeg==2.6.0" \
111 "pylibjpeg-rle==2.2.0"
113uv pip install "pyjpegls==1.5.1"
114uv pip install "Pillow==12.3.0"
115uv pip install "python-gdcm==3.2.6"
116```
118Install only what is required. Review transitive/package licensing:
119`pylibjpeg-libjpeg` has different licensing from MIT pydicom.
121Important stable documentation limitations include:
123- Pillow performs transformations that pydicom describes as not always
124 reversible and is not the preferred general decoder.
125- Pillow JPEG Extended support requires 8 Bits Allocated.
126- Pillow JPEG 2000 multi-sample support is constrained by bit depth.
127- GDCM has syntax/bit-depth limits; pydicom rejects known incorrect JPEG-LS
128 combinations for older GDCM releases.
129- `pylibjpeg-openjpeg` and other plugins have their own maximum bit depths.
130- pydicom's built-in RLE implementation is slower than compiled alternatives.
132Runtime check on macOS arm64, Python 3.13, pydicom 3.0.2 and GDCM 3.2.6:
133`pixel_array(..., decoding_plugin="gdcm")` correctly decoded the synthetic
134signed 12-bit JPEG-LS fixture, but `Dataset.decompress(decoding_plugin="gdcm")`
135failed with a read-only-array error during sign correction. Use the tested
136`decoding_plugin="pyjpegls"` for that decompression case. This is a scoped
137runtime finding, not a claim that every GDCM JPEG-LS image fails. RLE encoding
138also exposes a GDCM plugin in this release; new-backend RLE decoding does not.
140Never silently fall back in a validated workflow. Pin a plugin explicitly with
141`decoding_plugin=...`, record versions, and compare results against independent
142test vectors.

Stable 3.0.2 decompression plugins

The pydicom 3.0.2 pixels backend and plugin source expose these main choices (the older general handler guide includes legacy backend entries):

Transfer-syntax familyTypical pydicom plugin dependencies
Native/deflatedpydicom + NumPy
RLE Losslessbuilt-in pydicom; pylibjpeg-rle
JPEG Baseline/Extendedpylibjpeg-libjpeg; GDCM; Pillow with JPEG support
JPEG Losslesspylibjpeg-libjpeg; GDCM
JPEG-LSpyjpegls; pylibjpeg-libjpeg; GDCM
JPEG 2000pylibjpeg-openjpeg; GDCM; Pillow with OpenJPEG
HTJ2Kpylibjpeg-openjpeg

Pinned reviewed installations (Python 3.12+ for NumPy 2.5.3):

uv pip install "pydicom==3.0.2" "numpy==2.5.3"

uv pip install "pylibjpeg==2.1.0" \
  "pylibjpeg-libjpeg==2.4.0" \
  "pylibjpeg-openjpeg==2.6.0" \
  "pylibjpeg-rle==2.2.0"

uv pip install "pyjpegls==1.5.1"
uv pip install "Pillow==12.3.0"
uv pip install "python-gdcm==3.2.6"

Install only what is required. Review transitive/package licensing: pylibjpeg-libjpeg has different licensing from MIT pydicom.

Important stable documentation limitations include:

  • Pillow performs transformations that pydicom describes as not always reversible and is not the preferred general decoder.
  • Pillow JPEG Extended support requires 8 Bits Allocated.
  • Pillow JPEG 2000 multi-sample support is constrained by bit depth.
  • GDCM has syntax/bit-depth limits; pydicom rejects known incorrect JPEG-LS combinations for older GDCM releases.
  • pylibjpeg-openjpeg and other plugins have their own maximum bit depths.
  • pydicom's built-in RLE implementation is slower than compiled alternatives.

Runtime check on macOS arm64, Python 3.13, pydicom 3.0.2 and GDCM 3.2.6: pixel_array(..., decoding_plugin="gdcm") correctly decoded the synthetic signed 12-bit JPEG-LS fixture, but Dataset.decompress(decoding_plugin="gdcm") failed with a read-only-array error during sign correction. Use the tested decoding_plugin="pyjpegls" for that decompression case. This is a scoped runtime finding, not a claim that every GDCM JPEG-LS image fails. RLE encoding also exposes a GDCM plugin in this release; new-backend RLE decoding does not.

Never silently fall back in a validated workflow. Pin a plugin explicitly with decoding_plugin=..., record versions, and compare results against independent test vectors.

Raw lines 88 to 143
144## Frame-specific decoding
146Stable pydicom 3.0 adds path-based APIs that can reduce memory use:
148```python
149from pydicom.pixels import iter_pixels, pixel_array
151first = pixel_array("authorized/multiframe.dcm", index=0)
153for frame in iter_pixels(
154 "authorized/multiframe.dcm",
155 indices=[0, 2, 4],
156):
157 process_bounded_frame(frame)
158```
160Always calculate limits from:
162- Rows and Columns
163- Samples per Pixel
164- Bits Allocated and decoded NumPy item size
165- Number of Frames
166- expected intermediate arrays for rescale/window/color conversion
168The compressed file size is not a safe proxy for decoded memory. Metadata can
169also disagree with the codestream.
171Default decoding performs mandatory pixel unpacking and may convert YCbCr to
172RGB. `raw=True` suppresses optional color conversion, not mandatory processing
173such as bit unpacking.

Frame-specific decoding

Stable pydicom 3.0 adds path-based APIs that can reduce memory use:

from pydicom.pixels import iter_pixels, pixel_array

first = pixel_array("authorized/multiframe.dcm", index=0)

for frame in iter_pixels(
    "authorized/multiframe.dcm",
    indices=[0, 2, 4],
):
    process_bounded_frame(frame)

Always calculate limits from:

  • Rows and Columns
  • Samples per Pixel
  • Bits Allocated and decoded NumPy item size
  • Number of Frames
  • expected intermediate arrays for rescale/window/color conversion

The compressed file size is not a safe proxy for decoded memory. Metadata can also disagree with the codestream.

Default decoding performs mandatory pixel unpacking and may convert YCbCr to RGB. raw=True suppresses optional color conversion, not mandatory processing such as bit unpacking.

Raw lines 144 to 174
175## Decoder and encoder introspection
177```python
178from pydicom.pixels import get_decoder, get_encoder
179from pydicom.uid import JPEG2000Lossless
181decoder = get_decoder(JPEG2000Lossless)
182decoder_report = {
183 "available": decoder.is_available,
184 "plugins": decoder.available_plugins,
185 "missing": decoder.missing_dependencies,
188try:
189 encoder = get_encoder(JPEG2000Lossless)
190except NotImplementedError:
191 encoder = None
192```
194`is_available` means at least one implementation is importable. It does not
195guarantee support for every image or correctness of output.

Decoder and encoder introspection

from pydicom.pixels import get_decoder, get_encoder
from pydicom.uid import JPEG2000Lossless

decoder = get_decoder(JPEG2000Lossless)
decoder_report = {
    "available": decoder.is_available,
    "plugins": decoder.available_plugins,
    "missing": decoder.missing_dependencies,
}

try:
    encoder = get_encoder(JPEG2000Lossless)
except NotImplementedError:
    encoder = None

is_available means at least one implementation is importable. It does not guarantee support for every image or correctness of output.

Raw lines 175 to 196
197## In-place decompression behavior
199```python
200from pydicom import dcmread
202ds = dcmread("compressed.dcm")
203ds.decompress(
204 decoding_plugin="pylibjpeg",
205 generate_instance_uid=True,
207```
209`Dataset.decompress()`:
211- decodes and replaces Pixel Data in the dataset;
212- updates image-pixel metadata as needed;
213- sets Transfer Syntax UID to Explicit VR Little Endian;
214- generates a new SOP Instance UID by default;
215- may convert YCbCr to RGB by default (`as_rgb=False` controls this).
217This is a semantic modification. Write to a new file, keep source provenance,
218and use `enforce_file_format=True, overwrite=False`.

In-place decompression behavior

from pydicom import dcmread

ds = dcmread("compressed.dcm")
ds.decompress(
    decoding_plugin="pylibjpeg",
    generate_instance_uid=True,
)

Dataset.decompress():

  • decodes and replaces Pixel Data in the dataset;
  • updates image-pixel metadata as needed;
  • sets Transfer Syntax UID to Explicit VR Little Endian;
  • generates a new SOP Instance UID by default;
  • may convert YCbCr to RGB by default (as_rgb=False controls this).

This is a semantic modification. Write to a new file, keep source provenance, and use enforce_file_format=True, overwrite=False.

Raw lines 197 to 219
220## Compression behavior
222pydicom 3.0.2 directly exposes dataset compression for:
224- RLE Lossless (built-in pydicom and optional plugins)
225- JPEG-LS Lossless/Near-Lossless (`pyjpegls`)
226- JPEG 2000 Lossless/JPEG 2000 (`pylibjpeg-openjpeg`)
228```python
229from pydicom import dcmread, dcmwrite
230from pydicom.uid import RLELossless
232ds = dcmread("uncompressed.dcm")
233ds.compress(
234 RLELossless,
235 encoding_plugin="pydicom",
236 generate_instance_uid=True,
238dcmwrite("rle-derived.dcm", ds, enforce_file_format=True, overwrite=False)
239```
241Compression:
243- replaces Pixel Data with an encapsulated codestream;
244- updates Transfer Syntax UID;
245- generates a new SOP Instance UID by default;
246- requires Image Pixel attributes consistent with the encoded stream.
248Lossy compression decisions and clinical acceptability are outside pydicom and
249PS3.5. Record method, ratio, derivation, and quality effects according to the
250applicable IOD/workflow.

Compression behavior

pydicom 3.0.2 directly exposes dataset compression for:

  • RLE Lossless (built-in pydicom and optional plugins)
  • JPEG-LS Lossless/Near-Lossless (pyjpegls)
  • JPEG 2000 Lossless/JPEG 2000 (pylibjpeg-openjpeg)
from pydicom import dcmread, dcmwrite
from pydicom.uid import RLELossless

ds = dcmread("uncompressed.dcm")
ds.compress(
    RLELossless,
    encoding_plugin="pydicom",
    generate_instance_uid=True,
)
dcmwrite("rle-derived.dcm", ds, enforce_file_format=True, overwrite=False)

Compression:

  • replaces Pixel Data with an encapsulated codestream;
  • updates Transfer Syntax UID;
  • generates a new SOP Instance UID by default;
  • requires Image Pixel attributes consistent with the encoded stream.

Lossy compression decisions and clinical acceptability are outside pydicom and PS3.5. Record method, ratio, derivation, and quality effects according to the applicable IOD/workflow.

Raw lines 220 to 251
252## Encapsulation rules
254For encapsulated Pixel Data:
256- each frame is compressed separately;
257- frame codestreams are encapsulated into fragments;
258- Pixel Data VR is `OB`;
259- the dataset is explicit VR little endian at the dataset-structure level;
260- a Basic Offset Table may be empty;
261- Extended Offset Table/Lengths support large offsets, but require one fragment
262 per frame and an empty Basic Offset Table; they do not index multi-fragment frames.
264Access existing encapsulated data:
266```python
267from pydicom.encaps import generate_frames, get_frame
269extended = None
270if "ExtendedOffsetTable" in ds and "ExtendedOffsetTableLengths" in ds:
271 extended = (ds.ExtendedOffsetTable, ds.ExtendedOffsetTableLengths)
272frame0 = get_frame(
273 ds.PixelData,
274 0,
275 number_of_frames=int(ds.get("NumberOfFrames", 1)),
276 extended_offsets=extended,
279for encoded_frame in generate_frames(
280 ds.PixelData,
281 number_of_frames=int(ds.get("NumberOfFrames", 1)),
282 extended_offsets=extended,
283):
284 inspect_bounded_codestream(encoded_frame)
285```
287Create encapsulated Pixel Data from externally encoded frame bytes:
289```python
290from pydicom.encaps import encapsulate_extended
292pixel_data, offsets, lengths = encapsulate_extended(encoded_frames)
293ds.PixelData = pixel_data
294ds.ExtendedOffsetTable = offsets
295ds.ExtendedOffsetTableLengths = lengths
296ds["PixelData"].VR = "OB"
297ds["PixelData"].is_undefined_length = True
298```
300Set a matching Transfer Syntax UID and consistent Image Pixel metadata.
301`get_frame_offsets()`, `generate_pixel_data_frame()`, and other legacy
302encapsulation helpers are deprecated for removal in pydicom 4; use
303`parse_basic_offsets()`, `generate_fragments()`,
304`generate_fragmented_frames()`, and `generate_frames()`.

Encapsulation rules

For encapsulated Pixel Data:

  • each frame is compressed separately;
  • frame codestreams are encapsulated into fragments;
  • Pixel Data VR is OB;
  • the dataset is explicit VR little endian at the dataset-structure level;
  • a Basic Offset Table may be empty;
  • Extended Offset Table/Lengths support large offsets, but require one fragment per frame and an empty Basic Offset Table; they do not index multi-fragment frames.

Access existing encapsulated data:

from pydicom.encaps import generate_frames, get_frame

extended = None
if "ExtendedOffsetTable" in ds and "ExtendedOffsetTableLengths" in ds:
    extended = (ds.ExtendedOffsetTable, ds.ExtendedOffsetTableLengths)
frame0 = get_frame(
    ds.PixelData,
    0,
    number_of_frames=int(ds.get("NumberOfFrames", 1)),
    extended_offsets=extended,
)

for encoded_frame in generate_frames(
    ds.PixelData,
    number_of_frames=int(ds.get("NumberOfFrames", 1)),
    extended_offsets=extended,
):
    inspect_bounded_codestream(encoded_frame)

Create encapsulated Pixel Data from externally encoded frame bytes:

from pydicom.encaps import encapsulate_extended

pixel_data, offsets, lengths = encapsulate_extended(encoded_frames)
ds.PixelData = pixel_data
ds.ExtendedOffsetTable = offsets
ds.ExtendedOffsetTableLengths = lengths
ds["PixelData"].VR = "OB"
ds["PixelData"].is_undefined_length = True

Set a matching Transfer Syntax UID and consistent Image Pixel metadata. get_frame_offsets(), generate_pixel_data_frame(), and other legacy encapsulation helpers are deprecated for removal in pydicom 4; use parse_basic_offsets(), generate_fragments(), generate_fragmented_frames(), and generate_frames().

Raw lines 252 to 305
306## Writing and transfer-syntax conversion
308pydicom 3.0 resolves encoding in this priority:
3101. File Meta Information Transfer Syntax UID
3112. explicit `implicit_vr`/`little_endian` arguments
3123. deprecated dataset encoding flags
3134. original encoding
315```python
316from pydicom import dcmwrite
318dcmwrite(
319 "derived.dcm",
320 ds,
321 enforce_file_format=True,
322 overwrite=False,
324```
326Changing only `TransferSyntaxUID` does not compress/decompress Pixel Data.
327Likewise, `Dataset.save_as()` does not automatically convert between little and
328big endian. Use the documented pixel and writer APIs, then validate the
329derived instance.
331For a new integer pixel array, prefer `Dataset.set_pixel_data(array,
332photometric_interpretation="MONOCHROME2", bits_stored=12)` to manually editing
333`PixelData` and shape/bit tags. In 3.0.2 this accepts uint8/uint16/int8/int16,
334sets the Image Pixel attributes and a fresh SOP Instance UID by default, and
335sets Explicit VR Little Endian if the old syntax is absent or compressed.
336It rejects big-endian input datasets; it is not a float-pixel or boolean-array
337writer. A different image size still requires updates to geometry and other
338IOD attributes; the method does not supply a complete IOD.
340`LossyImageCompression` uses `00`/`01`, not YES/NO. A decompressed dataset can
341still have a lossy history: never infer that history from the current Transfer
342Syntax alone or reset `01` after decompressing/recompressing losslessly.

Writing and transfer-syntax conversion

pydicom 3.0 resolves encoding in this priority:

  1. File Meta Information Transfer Syntax UID
  2. explicit implicit_vr/little_endian arguments
  3. deprecated dataset encoding flags
  4. original encoding
from pydicom import dcmwrite

dcmwrite(
    "derived.dcm",
    ds,
    enforce_file_format=True,
    overwrite=False,
)

Changing only TransferSyntaxUID does not compress/decompress Pixel Data. Likewise, Dataset.save_as() does not automatically convert between little and big endian. Use the documented pixel and writer APIs, then validate the derived instance.

For a new integer pixel array, prefer Dataset.set_pixel_data(array, photometric_interpretation="MONOCHROME2", bits_stored=12) to manually editing PixelData and shape/bit tags. In 3.0.2 this accepts uint8/uint16/int8/int16, sets the Image Pixel attributes and a fresh SOP Instance UID by default, and sets Explicit VR Little Endian if the old syntax is absent or compressed. It rejects big-endian input datasets; it is not a float-pixel or boolean-array writer. A different image size still requires updates to geometry and other IOD attributes; the method does not supply a complete IOD.

LossyImageCompression uses 00/01, not YES/NO. A decompressed dataset can still have a lossy history: never infer that history from the current Transfer Syntax alone or reset 01 after decompressing/recompressing losslessly.

Raw lines 306 to 343
344## Validation checklist
346- Transfer Syntax UID is present, valid, and matches the encoded dataset.
347- SOP Class/Instance UIDs match File Meta Information.
348- Rows, Columns, Samples per Pixel, Bits Allocated/Stored, High Bit, Pixel
349 Representation, Photometric Interpretation, Planar Configuration, and
350 Number of Frames match the codestream.
351- Decoder/encoder plugin and version are recorded.
352- Frame count and decompressed memory are bounded before decode.
353- Lossy/lossless status and derivation attributes are correct.
354- Derived SOP Instance UID/provenance behavior is intentional.
355- Pixel values, frame order, color, signedness, modality transform, and VOI are
356 independently verified.
357- No diagnostic or conformance conclusion is based only on pydicom success.

Validation checklist

  • Transfer Syntax UID is present, valid, and matches the encoded dataset.
  • SOP Class/Instance UIDs match File Meta Information.
  • Rows, Columns, Samples per Pixel, Bits Allocated/Stored, High Bit, Pixel Representation, Photometric Interpretation, Planar Configuration, and Number of Frames match the codestream.
  • Decoder/encoder plugin and version are recorded.
  • Frame count and decompressed memory are bounded before decode.
  • Lossy/lossless status and derivation attributes are correct.
  • Derived SOP Instance UID/provenance behavior is intentional.
  • Pixel values, frame order, color, signedness, modality transform, and VOI are independently verified.
  • No diagnostic or conformance conclusion is based only on pydicom success.
Raw lines 344 to 358
359## Sources (verified 2026-10-01)
361- [pydicom 3.0.2 pixel plugin matrix](https://pydicom.github.io/pydicom/stable/guides/user/image_data_handlers.html)
362- [RLE decoder plugins](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.pixels.decoders.RLELosslessDecoder.html)
363- [RLE encoder plugins](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.pixels.encoders.RLELosslessEncoder.html)
364- [Lossy-compression history](https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_c.8.4.9.html)
365- [pydicom 3.0.2 Pixel Data API](https://pydicom.github.io/pydicom/stable/reference/pixels.html)
366- [Dataset pixel setter and UID behavior](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.dataset.Dataset.html)
367- [DICOM file writer contract](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.filewriter.dcmwrite.html)
368- [Pixel access tutorial](https://pydicom.github.io/pydicom/stable/tutorials/pixel_data/introduction.html)
369- [Compression/decompression tutorial](https://pydicom.github.io/pydicom/stable/tutorials/pixel_data/compressing.html)
370- [pydicom 3.0 release notes](https://pydicom.github.io/pydicom/stable/release_notes/index.html)
371- [DICOM PS3.3 Image Pixel Module](https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.3.html)
372- [DICOM PS3.5, Data Structures and Encoding](https://dicom.nema.org/medical/dicom/current/output/chtml/part05/PS3.5.html)
373- [DICOM PS3.5 encapsulated pixel transfer syntaxes](https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_A.4.html)
374- [DICOM PS3.6, Data Dictionary and UID registry](https://dicom.nema.org/medical/dicom/current/output/chtml/part06/PS3.6.html)
375- PyPI versions reviewed 2026-10-01:
376 [pydicom](https://pypi.org/project/pydicom/),
377 [NumPy](https://pypi.org/project/numpy/),
378 [Pillow](https://pypi.org/project/Pillow/),
379 [pylibjpeg](https://pypi.org/project/pylibjpeg/),
380 [pylibjpeg-libjpeg](https://pypi.org/project/pylibjpeg-libjpeg/),
381 [pylibjpeg-openjpeg](https://pypi.org/project/pylibjpeg-openjpeg/),
382 [pylibjpeg-rle](https://pypi.org/project/pylibjpeg-rle/),
383 [pyjpegls](https://pypi.org/project/pyjpegls/), and
384 [python-gdcm](https://pypi.org/project/python-gdcm/)