Repository: pydicom/pydicom

Issue:
Converting Dicom image to Png
**Describe the issue**
hi, i am trying to convert Dicom image to png but in case of some particular file i am getting this "list out of range error".

**Expected behavior**
dicom image converted to png pne

**Steps To Reproduce**
How to reproduce the issue. Please include:
1. A minimum working code sample
```
from pydicom import dcmread
def read_xray(path, voi_lut = True, fix_monochrome = True):
    dicom = dcmread(path, force=True)
    
    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to "human-friendly" view
    if voi_lut:
        data = apply_voi_lut(dicom.pixel_array, dicom)
    else:
        data = dicom.pixel_array
               
    # depending on this value, X-ray may look inverted - fix that:
    if fix_monochrome and dicom.PhotometricInterpretation == "MONOCHROME1":
        data = np.amax(data) - data
        
    data = data - np.min(data)
    data = data / np.max(data)
    data = (data * 255).astype(np.uint8)
        
    return data

img = read_xray('/content/a.5545da1153f57ff8425be6f4bc712c090e7e22efff194da525210c84aba2a947.dcm')
plt.figure(figsize = (12,12))
plt.imshow(img)
```
2. The traceback (if one occurred)
```
IndexError                                Traceback (most recent call last)
<ipython-input-13-6e53d7d16b90> in <module>()
     19     return data
     20 
---> 21 img = read_xray('/content/a.5545da1153f57ff8425be6f4bc712c090e7e22efff194da525210c84aba2a947.dcm')
     22 plt.figure(figsize = (12,12))
     23 plt.imshow(img)

2 frames
/usr/local/lib/python3.7/dist-packages/pydicom/multival.py in __getitem__(self, index)
     93         self, index: Union[slice, int]
     94     ) -> Union[MutableSequence[_ItemType], _ItemType]:
---> 95         return self._list[index]
     96 
     97     def insert(self, position: int, val: _T) -> None:

IndexError: list index out of range
```

3. Which of the following packages are available and their versions:
  * Numpy : latest as of 29th dec
  * Pillow : latest as of 29th dec
  * JPEG-LS : latest as of 29th dec
  * GDCM : latest as of 29th dec
4. The anonymized DICOM dataset (if possible).
image link : https://drive.google.com/file/d/1j13XTTPCLX-8e7FE--1n5Staxz7GGNWm/view?usp=sharing

**Your environment**
If you're using **pydicom 2 or later**, please use the `pydicom.env_info`
module to gather information about your environment and paste it in the issue:

```bash
$ python -m pydicom.env_info
```

For **pydicom 1.x**, please run the following code snippet and paste the
output.

```python
import platform, sys, pydicom
print(platform.platform(),
      "\nPython", sys.version,
      "\npydicom", pydicom.__version__)
```


Discussion:
pep8speaks: Hello @scaramallion! Thanks for opening this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:















* In the file [`pydicom/valuerep.py`](https://github.com/pydicom/pydicom/blob/621cafd5439839fee58449b1c1dbd209b8bbc400/pydicom/valuerep.py):

> [Line 58:80](https://github.com/pydicom/pydicom/blob/621cafd5439839fee58449b1c1dbd209b8bbc400/pydicom/valuerep.py#L58): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (88 > 79 characters)


codecov[bot]: # [Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
> Merging [#1555](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (621cafd) into [master](https://codecov.io/gh/pydicom/pydicom/commit/9db89e1d8f5e82dc617f9c8cbf303fe23a0632b9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (9db89e1) will **not change** coverage.
> The diff coverage is `100.00%`.

> :exclamation: Current head 621cafd differs from pull request most recent head ed9003f. Consider uploading reports for the commit ed9003f to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/pydicom/pydicom/pull/1555/graphs/tree.svg?width=650&height=150&src=pr&token=YciLV15Lwr&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)

```diff
@@           Coverage Diff           @@
##           master    #1555   +/-   ##
=======================================
  Coverage   97.42%   97.42%           
=======================================
  Files          66       66           
  Lines       10185    10185           
=======================================
  Hits         9923     9923           
  Misses        262      262           
```


| [Impacted Files](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) | Coverage Δ | |
|---|---|---|
| [pydicom/dataset.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9kYXRhc2V0LnB5) | `99.06% <100.00%> (ø)` | |
| [pydicom/encoders/base.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9lbmNvZGVycy9iYXNlLnB5) | `100.00% <100.00%> (ø)` | |
| [pydicom/filebase.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9maWxlYmFzZS5weQ==) | `97.58% <100.00%> (ø)` | |
| [pydicom/fileset.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9maWxlc2V0LnB5) | `100.00% <100.00%> (ø)` | |
| [pydicom/filewriter.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9maWxld3JpdGVyLnB5) | `97.79% <100.00%> (ø)` | |
| [pydicom/pixel\_data\_handlers/util.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9waXhlbF9kYXRhX2hhbmRsZXJzL3V0aWwucHk=) | `100.00% <100.00%> (ø)` | |
| [pydicom/valuerep.py](https://codecov.io/gh/pydicom/pydicom/pull/1555/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS92YWx1ZXJlcC5weQ==) | `99.20% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom). Last update [9db89e1...ed9003f](https://codecov.io/gh/pydicom/pydicom/pull/1555?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).


Patch:
diff --git a/pydicom/dataset.py b/pydicom/dataset.py
index 3b80942db..a4a212157 100644
--- a/pydicom/dataset.py
+++ b/pydicom/dataset.py
@@ -1488,7 +1488,7 @@ class Dataset:
         possible_handlers = [
             hh for hh in pydicom.config.pixel_data_handlers
             if hh is not None
-            and hh.supports_transfer_syntax(ts)  # type: ignore[attr-defined]
+            and hh.supports_transfer_syntax(ts)
         ]
 
         # No handlers support the transfer syntax
@@ -1504,7 +1504,7 @@ class Dataset:
         #   dependencies met
         available_handlers = [
             hh for hh in possible_handlers
-            if hh.is_available()  # type: ignore[attr-defined]
+            if hh.is_available()
         ]
 
         # There are handlers that support the transfer syntax but none of them
@@ -1518,13 +1518,13 @@ class Dataset:
             )
             pkg_msg = []
             for hh in possible_handlers:
-                hh_deps = hh.DEPENDENCIES  # type: ignore[attr-defined]
+                hh_deps = hh.DEPENDENCIES
                 # Missing packages
                 missing = [dd for dd in hh_deps if have_package(dd) is None]
                 # Package names
                 names = [hh_deps[name][1] for name in missing]
                 pkg_msg.append(
-                    f"{hh.HANDLER_NAME} "  # type: ignore[attr-defined]
+                    f"{hh.HANDLER_NAME} "
                     f"(req. {', '.join(names)})"
                 )
 
@@ -1819,7 +1819,7 @@ class Dataset:
 
         available_handlers = [
             hh for hh in overlay_data_handlers
-            if hh.is_available()  # type: ignore[attr-defined]
+            if hh.is_available()
         ]
         if not available_handlers:
             # For each of the handlers we want to find which
@@ -1830,13 +1830,13 @@ class Dataset:
             )
             pkg_msg = []
             for hh in overlay_data_handlers:
-                hh_deps = hh.DEPENDENCIES  # type: ignore[attr-defined]
+                hh_deps = hh.DEPENDENCIES
                 # Missing packages
                 missing = [dd for dd in hh_deps if have_package(dd) is None]
                 # Package names
                 names = [hh_deps[name][1] for name in missing]
                 pkg_msg.append(
-                    f"{hh.HANDLER_NAME} "  # type: ignore[attr-defined]
+                    f"{hh.HANDLER_NAME} "
                     f"(req. {', '.join(names)})"
                 )
 
@@ -1846,7 +1846,7 @@ class Dataset:
         for handler in available_handlers:
             try:
                 # Use the handler to get an ndarray of the pixel data
-                func = handler.get_overlay_array  # type: ignore[attr-defined]
+                func = handler.get_overlay_array
                 return cast("numpy.ndarray", func(self, group))
             except Exception as exc:
                 logger.debug(
diff --git a/pydicom/encoders/base.py b/pydicom/encoders/base.py
index 44044b10b..d41fda43b 100644
--- a/pydicom/encoders/base.py
+++ b/pydicom/encoders/base.py
@@ -84,11 +84,11 @@ class Encoder:
         module = import_module(import_path[0])
 
         # `is_available(UID)` is required for plugins
-        if module.is_available(self.UID):  # type: ignore[attr-defined]
+        if module.is_available(self.UID):
             self._available[label] = getattr(module, import_path[1])
         else:
             # `ENCODER_DEPENDENCIES[UID]` is required for plugins
-            deps = module.ENCODER_DEPENDENCIES  # type: ignore[attr-defined]
+            deps = module.ENCODER_DEPENDENCIES
             self._unavailable[label] = deps[self.UID]
 
     @staticmethod
diff --git a/pydicom/filebase.py b/pydicom/filebase.py
index cec6bdcc0..9ac039045 100644
--- a/pydicom/filebase.py
+++ b/pydicom/filebase.py
@@ -182,15 +182,15 @@ class DicomFileLike(DicomIO):
         self.close = file_like_obj.close
         self.name: str = getattr(file_like_obj, 'name', '<no filename>')
 
-    def no_write(self, bytes_read: bytes) -> None:
+    def no_write(self, bytes_read: bytes) -> int:
         """Used for file-like objects where no write is available"""
         raise IOError("This DicomFileLike object has no write() method")
 
-    def no_read(self, bytes_read: Optional[int] = None) -> None:
+    def no_read(self, size: int = -1) -> bytes:
         """Used for file-like objects where no read is available"""
         raise IOError("This DicomFileLike object has no read() method")
 
-    def no_seek(self, offset: int, from_what: int = 0) -> None:
+    def no_seek(self, offset: int, whence: int = 0) -> int:
         """Used for file-like objects where no seek is available"""
         raise IOError("This DicomFileLike object has no seek() method")
 
diff --git a/pydicom/fileset.py b/pydicom/fileset.py
index 436e1e903..a831318f3 100644
--- a/pydicom/fileset.py
+++ b/pydicom/fileset.py
@@ -2865,10 +2865,10 @@ _FOUR_LEVEL_SOP_CLASSES = {
 
 def _single_level_record_type(ds: Dataset) -> str:
     """Return a single-level *Directory Record Type* for `ds`."""
-    sop_class = getattr(ds, "SOPClassUID", None)
+    sop_class = cast(Optional[UID], getattr(ds, "SOPClassUID", None))
 
     try:
-        return _SINGLE_LEVEL_SOP_CLASSES[sop_class]
+        return _SINGLE_LEVEL_SOP_CLASSES[sop_class]  # type: ignore[index]
     except KeyError:
         return "PATIENT"
 
@@ -2888,9 +2888,9 @@ def _four_level_record_type(ds: Dataset) -> str:
     if "RTPlanLabel" in ds:
         return "RT PLAN"
 
-    sop_class = getattr(ds, "SOPClassUID", None)
+    sop_class = cast(Optional[UID], getattr(ds, "SOPClassUID", None))
 
     try:
-        return _FOUR_LEVEL_SOP_CLASSES[sop_class]
+        return _FOUR_LEVEL_SOP_CLASSES[sop_class]  # type: ignore[index]
     except KeyError:
         return "IMAGE"
diff --git a/pydicom/filewriter.py b/pydicom/filewriter.py
index ab164a61b..2a9bdee73 100644
--- a/pydicom/filewriter.py
+++ b/pydicom/filewriter.py
@@ -1000,6 +1000,7 @@ def dcmwrite(
         Write a DICOM file from a dataset that was read in with ``dcmread()``.
         ``save_as()`` wraps ``dcmwrite()``.
     """
+    tsyntax: Optional[UID]
 
     # Ensure is_little_endian and is_implicit_VR are set
     if None in (dataset.is_little_endian, dataset.is_implicit_VR):
@@ -1090,7 +1091,7 @@ def dcmwrite(
             fp.write(preamble)
             fp.write(b'DICM')
 
-        tsyntax: Optional[UID] = None  # type: ignore[no-redef]
+        tsyntax = None
         if dataset.file_meta:  # May be an empty Dataset
             # If we want to `write_like_original`, don't enforce_standard
             write_file_meta_info(
diff --git a/pydicom/pixel_data_handlers/util.py b/pydicom/pixel_data_handlers/util.py
index b5f79bfc1..fe08752a8 100644
--- a/pydicom/pixel_data_handlers/util.py
+++ b/pydicom/pixel_data_handlers/util.py
@@ -244,7 +244,7 @@ def apply_modality_lut(arr: "np.ndarray", ds: "Dataset") -> "np.ndarray":
     * DICOM Standard, Part 4, :dcm:`Annex N.2.1.1
       <part04/sect_N.2.html#sect_N.2.1.1>`
     """
-    if 'ModalityLUTSequence' in ds:
+    if ds.get("ModalityLUTSequence"):
         item = cast(List["Dataset"], ds.ModalityLUTSequence)[0]
         nr_entries = cast(List[int], item.LUTDescriptor)[0] or 2**16
         first_map = cast(List[int], item.LUTDescriptor)[1]
@@ -342,7 +342,7 @@ def apply_voi_lut(
       <part04/sect_N.2.html#sect_N.2.1.1>`
     """
     valid_voi = False
-    if 'VOILUTSequence' in ds:
+    if ds.get('VOILUTSequence'):
         ds.VOILUTSequence = cast(List["Dataset"], ds.VOILUTSequence)
         valid_voi = None not in [
             ds.VOILUTSequence[0].get('LUTDescriptor', None),
@@ -408,7 +408,7 @@ def apply_voi(
     * DICOM Standard, Part 4, :dcm:`Annex N.2.1.1
       <part04/sect_N.2.html#sect_N.2.1.1>`
     """
-    if "VOILUTSequence" not in ds:
+    if not ds.get('VOILUTSequence'):
         return arr
 
     if not np.issubdtype(arr.dtype, np.integer):
@@ -529,7 +529,7 @@ def apply_windowing(
     ds.BitsStored = cast(int, ds.BitsStored)
     y_min: float
     y_max: float
-    if 'ModalityLUTSequence' in ds:
+    if ds.get('ModalityLUTSequence'):
         # Unsigned - see PS3.3 C.11.1.1.1
         y_min = 0
         item = cast(List["Dataset"], ds.ModalityLUTSequence)[0]
diff --git a/pydicom/valuerep.py b/pydicom/valuerep.py
index aac1d4612..cc0c1a986 100644
--- a/pydicom/valuerep.py
+++ b/pydicom/valuerep.py
@@ -55,7 +55,10 @@ class _DateTimeBase:
     def __setstate__(self, state: Dict[str, Any]) -> None:
         self.__dict__.update(state)
 
-    def __reduce_ex__(self, protocol: int) -> Tuple[Any, ...]:
+    def __reduce_ex__(  # type: ignore[override]
+        self, protocol: int
+    ) -> Tuple[Any, ...]:
+        # Python 3.8 - protocol: SupportsIndex (added in 3.8)
         # datetime.time, and datetime.datetime return Tuple[Any, ...]
         # datetime.date doesn't define __reduce_ex__
         reduce_ex = cast(Tuple[Any, ...], super().__reduce_ex__(protocol))
@@ -324,17 +327,17 @@ class TM(_DateTimeBase, datetime.time):
             if match.group('ms'):
                 microsecond = int(match.group('ms').rstrip().ljust(6, '0'))
 
-            return super().__new__(  # type: ignore[call-arg, no-any-return]
+            return super().__new__(
                 cls, hour, minute, second, microsecond
             )
 
         if isinstance(val, datetime.time):
-            return super().__new__(  # type: ignore[call-arg, no-any-return]
+            return super().__new__(
                 cls, val.hour, val.minute, val.second, val.microsecond
             )
 
         try:
-            return super().__new__(  # type: ignore[call-arg, no-any-return]
+            return super().__new__(
                 cls, *args, **kwargs
             )
         except Exception as exc:
@@ -925,7 +928,7 @@ class PersonName:
         if len(args) and args[0] is None:
             return None
 
-        return cast("PersonName", super().__new__(cls))
+        return super().__new__(cls)
 
     def __init__(
         self,


Test patch:
diff --git a/pydicom/tests/test_handler_util.py b/pydicom/tests/test_handler_util.py
index e5357017d..89566be44 100644
--- a/pydicom/tests/test_handler_util.py
+++ b/pydicom/tests/test_handler_util.py
@@ -890,6 +890,10 @@ class TestNumpy_ModalityLUT:
         out = apply_modality_lut(arr, ds)
         assert arr is out
 
+        ds.ModalityLUTSequence = []
+        out = apply_modality_lut(arr, ds)
+        assert arr is out
+
     def test_lutdata_ow(self):
         """Test LUT Data with VR OW."""
         ds = dcmread(MOD_16_SEQ)
@@ -1839,6 +1843,10 @@ class TestNumpy_ApplyWindowing:
         out = apply_windowing(arr, ds)
         assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
 
+        ds.ModalityLUTSequence = []
+        out = apply_windowing(arr, ds)
+        assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
+
     def test_rescale_empty(self):
         """Test RescaleSlope and RescaleIntercept being empty."""
         ds = dcmread(WIN_12_1F)
@@ -2051,6 +2059,11 @@ class TestNumpy_ApplyVOI:
         out = apply_voi(arr, ds)
         assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
 
+        ds.VOILUTSequence = []
+        out = apply_voi(arr, ds)
+        assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
+
+
     def test_voi_lutdata_ow(self):
         """Test LUT Data with VR OW."""
         ds = Dataset()
@@ -2072,6 +2085,7 @@ class TestNumpy_ApplyVOI:
 
 @pytest.mark.skipif(not HAVE_NP, reason="Numpy is not available")
 class TestNumpy_ApplyVOILUT:
+    """Tests for util.apply_voi_lut()"""
     def test_unchanged(self):
         """Test input array is unchanged if no VOI LUT"""
         ds = Dataset()
@@ -2082,6 +2096,10 @@ class TestNumpy_ApplyVOILUT:
         out = apply_voi_lut(arr, ds)
         assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
 
+        ds.VOILUTSequence = []
+        out = apply_voi_lut(arr, ds)
+        assert [-128, -127, -1, 0, 1, 126, 127] == out.tolist()
+
     def test_only_windowing(self):
         """Test only windowing operation elements present."""
         ds = Dataset()