# Version 3.0.0

The major breaking changes with the version 3.0 release are:

> - The value for `JPEGLossless` has changed to `1.2.840.10008.1.2.4.57`.

> - The encoding used when saving datasets defaults to the set Transfer Syntax UID.

> - `Dataset.pixel_array` will convert YCbCr Pixel Data to RGB by default when possible.

> - `read_file` and `write_file` have been removed.

## Changes

- Removed support for Python <= 3.9.

- All tag formats changed to upper case, no space e.g. “(7FE0,0010)” rather than “(7fe0, 0010)”.

- Values with VR AE with an incorrect value length are now handled gracefully (extra bytes are ignored with a warning).

- A value of 0 for Number of Frames is now handled as 1 frame, with a user warning issued on reading the pixel data (#1844).

- The value for `JPEGLossless` has changed from 1.2.840.10008.1.2.4.70 to 1.2.840.10008.1.2.4.57 to match its UID keyword. Use `JPEGLosslessSV1` instead for 1.2.840.10008.1.2.4.70.

- The theoretical maximum number of instances supported by `FileSet` has been reduced to 1838265625 to ensure support for 32-bit systems (#1743).

- The characters used by `generate_filename()` when alphanumeric is `True` has been reduced to [0-9][A-I,K-Z].

- `get_testdata_file()` and `get_testdata_files()` now raise `ValueError` if called with an absolute path or pattern.

- `generate_uid()` has been changed to use a random suffix generated using `randbelow()` when entropy_srcs isn’t used, and the maximum allowed length of the prefix has been changed to 54 characters (#1773).

- `DataElement.VM` always returns `1` for SQ elements (#1481).

- DICOM dictionary updated to 2024c.

- Concepts dictionaries updated to 2024c.

- `validate_file_meta()` now checks to ensure required Type 1 elements aren’t empty.

- implicit_vr and little_endian optional arguments added to `Dataset.save_as()`. In addition, this method will now raise an exception if the user tries to convert between little and big endian datasets. If this is something you need, use `dcmwrite()` instead.

- Added the overwrite argument to `Dataset.save_as()` and `dcmwrite()` to allow raising a `FileExistsError` if trying to write to a file that already exists (#2104).

- implicit_vr, little_endian and force_encoding optional arguments added to `dcmwrite()`.

- The priority used to decide which encoding to use with `Dataset.save_as()` and `dcmwrite()` has been changed to:

  1. The set Transfer Syntax UID,

  2. The implicit_vr and little_endian arguments,

  3. `Dataset.is_implicit_VR` and `Dataset.is_little_endian`,

  4. `Dataset.original_encoding`.

- Datasets containing Command Set (0000,eeee) elements can no longer be written using `Dataset.save_as()` or `dcmwrite()`, use `write_dataset()` instead.

- A dataset’s `file_meta` elements are no longer modified when writing.

- `DicomIO` now requires a readable or writeable buffer during initialisation and `DicomBytesIO` directly inherits from it.

- The `pydicom.encoders` module has been moved to `pydicom.pixels.encoders`, the original import path will be removed in v4.0.

- Using GDCM v3.0.23 or lower to decode JPEG-LS datasets with a Bits Stored of 6 or 7 produces incorrect results, so attempting to do so now raises an exception. `pyjpegls` or `pylibjpeg` with `pylibjpeg-libjpeg` can be used instead (#2008).

- Using Pillow with JPEG 2000 encoded > 8-bit multi-sample data (such as RGB) now raises an exception as Pillow cannot decode such data correctly (#2006).

- An exception will now be raised if an `ndarray` is used to set Pixel Data (#50).

- Logging of errors when converting elements using `Dataset.to_json_dict()` have been made more verbose and now use `logging.WARNING` (#1909).

- Added `FileDataset.buffer` and changed `FileDataset.filename` to only be the filename the dataset was read from (if any) (#1937).

### Removals

- The `compat` module has been removed.

- The `dicomdir` module and `DicomDir` class have been removed and reading a DICOMDIR dataset now returns a normal `FileDataset` instance. For handling DICOM File-sets and DICOMDIR datasets use the `FileSet` class instead.

- The `read_file` and `write_file` functions have been removed, use `dcmread()` and `dcmwrite()` instead.

- The following UID constants have been removed:

  > - `JPEGBaseline` (use `JPEGBaseline8Bit` instead)

  > - `JPEGExtended` (use `JPEGExtended12Bit` instead)

  > - `JPEGLSLossy` (use `JPEGLSNearLossless` instead)

  > - `JPEG2000MultiComponentLossless` (use `JPEG2000MCLossless` instead)

  > - `JPEG2000MultiComponent` (use `JPEG2000MC` instead)

- The following UID lists have been removed:

  > - `JPEGLossyCompressedPixelTransferSyntaxes`: use `JPEGTransferSyntaxes`

  > - `JPEGLSSupportedCompressedPixelTransferSyntaxes`: use `JPEGLSTransferSyntaxes`

  > - `JPEG2000CompressedPixelTransferSyntaxes`: use `JPEG2000TransferSyntaxes`

  > - `RLECompressedLosslessSyntaxes`: use `RLETransferSyntaxes`

  > - `UncompressedPixelTransferSyntaxes`: use `UncompressedTransferSyntaxes`

  > - `PILSupportedCompressedPixelTransferSyntaxes`

- The `PersonNameUnicode` class has been removed, use `PersonName` instead.

- The `DataElement.description` attribute has been removed, use `DataElement.name` instead.

- The `pixel_data_handlers.rle_handler.rle_encode_frame` function has been removed, use `Dataset.compress()` or `RLELosslessEncoder` instead.

- The `_storage_sopclass_uids` module has been removed, import UIDs from the `uid` module instead.

- The following properties have been removed:

  > - `Dataset.parent` and `Dataset.parent_seq`

  > - `Sequence.parent` and `Sequence.parent_dataset`

  > - `DataElement.parent`

- The `overlay_data_handlers` module has been removed, use the `overlays` module instead.

- `config.overlay_data_handlers` has been removed.

- `Dataset.fix_meta_info()` has been removed as encoding state now follows the transfer syntax instead of the other way around.

## Enhancements

- Added details of missing required tag information when adding a dataset to a File-set (#1752).

- The following UID constants have been added:

  > - `MPEG2MPMLF`

  > - `MPEG2MPHLF`

  > - `MPEG4HP41F`

  > - `MPEG4HP41BDF`

  > - `MPEG4HP422DF`

  > - `MPEG4HP423DF`

  > - `MPEG4HP42STEREOF`

  > - `HTJ2KLossless`

  > - `HTJ2KLosslessRPCL`

  > - `HTJ2K`

  > - `JPIPHTJ2KReferenced`

  > - `JPIPHTJ2KReferencedDeflate`

  > - `SMPTEST211020UncompressedProgressiveActiveVideo`

  > - `SMPTEST211020UncompressedInterlacedActiveVideo`

  > - `SMPTEST211030PCMDigitalAudio`

- Added convenience method `add_new_private()` to add a private tag.

- Added the examples module to make it easier and less confusing for users to work with the example datasets used by the documentation.

- Added the ability to set the corresponding dataset encoding for private transfer syntaxes to `UID` via the `set_private_encoding()` method.

- Added the ability to register private transfer syntaxes with `register_transfer_syntax()` so they can be used when reading datasets with `dcmread()`.

- Warning messages are also sent to the pydicom logger (#1529).

- Added the following to the `encaps` module:

  - `parse_basic_offsets()` for parsing the Basic Offset Table.

  - `parse_fragments()` for determining the number of encapsulated fragments and their byte offsets.

  - `generate_fragments()` for yielding encapsulated fragments.

  - `generate_fragmented_frames()` for yielding encapsulated frame fragments.

  - `generate_frames()` for yielding whole encapsulated frames.

  - `get_frame()` for returning the specific encapsulated frame at index without necessarily having to read the preceding frames into memory.

  These new functions support reading encapsulated data from both `bytes` or any Python object with `read()`, `seek()` and `tell()` methods such as `io.BytesIO`, `BinaryIO` or `mmap.mmap`. They also support using the Extended Offset Table for determining frame boundaries.

- Added the keep_deferred keyword argument to `Dataset.get_item()` to allow accessing the file offset and element length without having to read the element value. (#1873).

- Added the `pixels` module and a new more flexible backend for decoding pixel data via `Decoder` factory class instances. The new decoding backend adds support for the following:

  - Returning a view over the original pixel data buffer (#746).

  - Retrieving specific frames (#1263, #1243).

  - Returning RGB pixel data by default for JPEG (#1781, #1133 and many others).

  - Returning excess frames for JPEG when there is no Basic or Extended Offset Table and the Number of Frames is incorrect (#1666).

  - Returning excess frames for native encoding when the Number of Frames is incorrect (#2035)

  - Returning the decoded pixel data as either a NumPy `ndarray` or buffer-like object.

  - Iterating through either all or specific frames.

- Added support for decoding HTJ2K transfer syntaxes (#1848).

- Added two functions for returning pixel data as a NumPy `ndarray` from a path to a dataset while minimizing memory-usage: `pixel_array()` and `iter_pixels()`.

- Added two functions for compressing and decompressing datasets using the new decoding backend: `compress()` and `decompress()`.

- Added support for the following transfer syntaxes to `Dataset.compress()` (#1997):

  - JPEG-LS Lossless with `JPEGLSLosslessEncoder`

  - JPEG-LS Near Lossless with `JPEGLSNearLosslessEncoder`

  - JPEG 2000 Lossless with `JPEG2000LosslessEncoder`

  - JPEG 2000 with `JPEG2000Encoder`

  See the JPEG-LS and JPEG 2000 encoding guides for more information.

- Added `Dataset.pixel_array_options()` for controlling pixel data decoding when using `Dataset.pixel_array` with the new `pixels` backend.

- Improve support for reading and resolving inline binary data with VR=UN from Json (#2062).

- `get_j2k_parameters()` now takes into account the JP2 header (if present, although it’s non-conformant for it to be) (#2073).

- Added support for NumPy v2.0 (#2075).

- Added `pydicom.__concepts_version__` attribute with the DICOM Standard version used to create the concepts dictionaries in `pydicom.sr` (#1021).

- Refactored the interface for the concepts in `pydicom.sr` to simplify the access types (#1454).

- Added the `Dataset.set_pixel_data()` method and `set_pixel_data()` function for automatically setting a dataset’s Pixel Data and related Image Pixel module elements using an `ndarray` (#50).

- Added typing support for `Dataset` element access using the types-pydicom package. (#1485).

- Added `apply_presentation_lut()` for applying a Presentation LUT to an `ndarray` (#1265).

- Added `apply_icc_profile()` and `create_icc_transform()` for applying ICC profiles to an `ndarray` (#1244).

- Added `Dataset.update_raw_element()` to make it easier to modify a `RawDataElement`’s VR or value prior to conversion to a `DataElement` (#1739).

- Added support for using `io.BufferedIOBase` subclasses to set the value for elements with O* VRs such as OB and OW (#1913).

- Added `encapsulate_buffer()` and `encapsulate_extended_buffer()` for encapsulating buffered compressed Pixel Data via `EncapsulatedBuffer` instances.

- Added elements with OB, OD, OF, OL, OW, OV VRs to the type validation checking when setting `DataElement` values (#1414).

- Added `convert_raw_data_element()` for converting raw element data to `DataElement` instances.

- Added the `hooks` module which contains an interface for adding callback functions via the `Hooks` singleton, as well as default and alternative convenience callbacks for `convert_raw_data_element()` (#1556).

## Fixes

- Fixed the GDCM and pylibjpeg handlers changing the Pixel Representation value to 0 when the J2K stream disagrees with the dataset and `APPLY_J2K_CORRECTIONS` is `True` (#1689).

- Fixed pydicom codify error when relative path did not exist.

- Fixed the VR enum sometimes returning invalid values for Python 3.11+ (#1874).

- Fixed pixel data handler for Pillow 10.1 raising an AttributeError (#1907).

- Fixed a possible security issue with `FileInstance` instances being able to escape the temporary directory when being added to a `FileSet` (#1922).

- Fixed an `AttributeError` when running `deepcopy()` after `Dataset.update` (#1816).

- Fixed `encapsulate_extended()` not returning the correct values for odd-length frames (#1968).

- Fixed using the incorrect encoding when writing datasets converted between explicit and implicit VR when only the Transfer Syntax UID was changed (#1943).

- Fixed the `jpeg_ls`, `pillow` and `rle` pixel data handlers not working correctly when a frame is spread across multiple fragments (#1774).

- Added mitigation for a rare case where clearing the pixel data value prior to updating it may sometimes result in `pixel_array` returning the previous array instead of creating a new one (#1983).

- Fixed a `KeyError` when comparing codes with one of the codes having `scheme_designator` set to `SRT` but not being included in the `SRT` to `SCT` code mapping (#1994).

- Fixed JPEG-LS datasets with a Pixel Representation of 1 returning incorrect image data when Bits Stored is less than Bits Allocated (#2009).

- Fixed decoding failures for JPEG-LS datasets with Bits Allocated of 16 and Bits Stored <= 8 (#2010).

- Fixed the Pixel Data VR not being set correctly with `Dataset.compress()` (#2013).

- Fixed `Dataset.decompress()` not updating the Pixel Data element value until after saving (#2024).

- Fixed a rare issue with converting pixel data to an `ndarray` when Bits Stored is less than Bits Allocated and the unused bits haven’t been set to an appropriate value for correct interpretation of the data.

- Fixed a `RecursionError` when using `copy.deepcopy()` with a dataset containing a private block (#2025).

- Fixed non-unique keywords for the concept codes in `pydicom.sr` (#1388).

- Fixed keywords using Python identifiers in `pydicom.sr` (#1273).

- Fixed being unable to write LUT Descriptor when the VR is SS and the first value is greater than 32767 (#2081).

- Fixed Deflated Explicit VR Little Endian datasets not working correctly with `codify` (#1937).

## Deprecations

- `Dataset.is_little_endian` and `Dataset.is_implicit_VR` will be removed in v4.0.

- `Dataset.read_little_endian` and `Dataset.read_implicit_vr` will be removed in v4.0, use `Dataset.original_encoding` instead.

- `Dataset.read_encoding` will be removed in v4.0, use `Dataset.original_character_set` instead.

- The write_like_original optional argument to `Dataset.save_as` and `dcmwrite()` will be removed in v4.0, use enforce_file_format instead.

- The following `encaps` module functions will be removed in v4.0:

  - `get_frame_offsets()`, use `parse_basic_offsets()` instead.

  - `generate_pixel_data_fragment()`, use `generate_fragments()` instead.

  - `generate_pixel_data_frame()`, use `generate_fragmented_frames()` instead.

  - `generate_pixel_data()`, use `generate_frames()` instead.

  - `decode_data_sequence()`, use `generate_fragments()` instead.

  - `defragment_data()`, use `generate_frames()` instead.

  - `read_item()`, use `generate_fragments()` instead.

- The `pydicom.pixel_data_handlers` module will be removed in v4.0. All pixel data processing will use the `pydicom.pixels` module instead starting with v3.0.

  > - The following functions from `pydicom.pixel_data_handlers.util` have been moved to `pydicom.pixels.processing`:

  >   - `apply_color_lut()`

  >   - `apply_modality_lut()`

  >   - `apply_rescale()`

  >   - `apply_voi_lut()`

  >   - `apply_voi()`

  >   - `apply_windowing()`

  >   - `convert_color_space()`

  > - The following functions from `pydicom.pixel_data_handlers.util` have been moved to `pydicom.pixels.utils`:

  >   - `expand_ybr422()`

  >   - `get_expected_length()`

  >   - `get_image_pixel_ids()`

  >   - `get_j2k_parameters()`

  >   - `get_nr_frames()`

  >   - `pack_bits()`

  >   - `pixel_dtype()`

  >   - `reshape_pixel_array()`

  >   - `unpack_bits()`

  > - `pydicom.pixel_data_handlers.util.dtype_corrected_for_endianness()` will be removed in v4.0.

- `Dataset.convert_pixel_data()` will be removed in v4.0, use `Dataset.pixel_array_options()` instead.

- `DataElement_from_raw()` will be removed in v4.0, please use `convert_raw_data_element()` instead.

- `config.data_element_callback` and `config.data_element_callback_kwargs` will be removed in v4.0, please use the hooks for `convert_raw_data_element()` instead.

- The `pydicom.utils.fixers` submodule will be removed in v4.0, please use the alternative callbacks for `convert_raw_data_element()` in the `hooks` module instead.

## Pydicom Internals

- Repository folder structure refactored.

- Renamed top level `source` folder to `util`.

- New CI tools - dependabot, and pre-commit using black and ruff.