Repository: pydicom/pydicom

Issue:
Floating point images data incorrectly require the BitsStored attribute to decode

**Describe the issue**

The new pixel decompression logic in pydicom 3 requires that the `BitsStored` attribute be present for all images. While `BitsStored` is required for all images that use the `Image Pixel` module, "floating point" images that use the [Floating Point Image Pixel](https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.24.html#sect_C.7.6.24) and [Double Floating Point Image Pixel](https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.25.html#sect_C.7.6.25) modules should *not* include this attribute (I suppose this is because for floating point number the number of bits stored is always equal to the number of bits allocated).

Essentially this means that floating point images that correctly omit the BitsStored cannot be decoded by pydicom 3 now (but the same images could be decoded in pydicom 2).

**Steps to reproduce**

I have attached a small parametric map image using Double Float Pixel Data that demonstrates the problem. If you read it and use the `.pixel_array` property, you get this error message on pydicom 3:
[parametric_map_example.zip](https://github.com/user-attachments/files/17006688/parametric_map_example.zip)


```
Traceback (most recent call last):
  File "/home/chris/repos/highdicom/pm_reading.py", line 5, in <module>
    dcm.pixel_array
  File "/home/chris/repos/pydicom/src/pydicom/dataset.py", line 918, in __getattr__
    return object.__getattribute__(self, name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/repos/pydicom/src/pydicom/dataset.py", line 2193, in pixel_array
    self.convert_pixel_data()
  File "/home/chris/repos/pydicom/src/pydicom/dataset.py", line 1726, in convert_pixel_data
    self._pixel_array = pixel_array(self, **opts)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/repos/pydicom/src/pydicom/pixels/utils.py", line 1430, in pixel_array
    return decoder.as_array(
           ^^^^^^^^^^^^^^^^^
  File "/home/chris/repos/pydicom/src/pydicom/pixels/decoders/base.py", line 989, in as_array
    runner.validate()
  File "/home/chris/repos/pydicom/src/pydicom/pixels/decoders/base.py", line 752, in validate
    self._validate_options()
  File "/home/chris/repos/pydicom/src/pydicom/pixels/decoders/base.py", line 821, in _validate_options
    super()._validate_options()
  File "/home/chris/repos/pydicom/src/pydicom/pixels/common.py", line 576, in _validate_options
    raise AttributeError(f"{prefix},0101) 'Bits Stored'")
AttributeError: Missing required element: (0028,0101) 'Bits Stored'. Did you mean: '_pixel_array'?
```

**Possible solution**

The simplest solution (though not especially elegant) is just to copy the BitsAllocated to BitsStored in case it's missing. I.e. replacing this part of `src/pydicom/pixels/common.py` (line 575):

```python
        if self._opts.get("bits_stored") is None:
            raise AttributeError(f"{prefix},0101) 'Bits Stored'")
```

with something like this

```python
        if self._opts.get("bits_stored") is None:
            # The BitsStored attribute is not present for images using the
            # "Floating Point Image Pixel" and "Double Floating Point Image
            # Pixel" modules, but is implicitly equal to the BitsAllocated
            if self._opts.get("pixel_keyword") in (
                "FloatPixelData",
                "DoubleFloatPixelData"
            ):
                self.set_option('bits_stored', self.bits_allocated)
            else:
                raise AttributeError(f"{prefix},0101) 'Bits Stored'")

```

Though I suppose you may also want this to be fixed at a lower level. I'm happy to help draft a PR when we have the right direction.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pydicom/pydicom/pull/2136?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 98.38%. Comparing base [(`f123075`)](https://app.codecov.io/gh/pydicom/pydicom/commit/f123075c0f8eee56476f068fc8c519bda00fc2ca?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) to head [(`4f0941b`)](https://app.codecov.io/gh/pydicom/pydicom/commit/4f0941b9ec02b58ca58cce2025a3cc0fe8b1117b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #2136      +/-   ##
==========================================
+ Coverage   98.34%   98.38%   +0.04%     
==========================================
  Files          77       77              
  Lines       13435    13437       +2     
==========================================
+ Hits        13212    13220       +8     
+ Misses        223      217       -6     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pydicom/pydicom/pull/2136?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).


Patch:
diff --git a/doc/release_notes/v3.1.0.rst b/doc/release_notes/v3.1.0.rst
index 986843ac9..a2159496b 100644
--- a/doc/release_notes/v3.1.0.rst
+++ b/doc/release_notes/v3.1.0.rst
@@ -7,3 +7,5 @@ Fixes
 * Changed logging of missing plugin imports to use :attr:`logging.DEBUG` (:issue:`2128`).
 * Include all :mod:`~pydicom.examples` module datasets with the package (:issue:`2128`, :issue:`2131`)
 * Fixed an invalid VR value in the private data dictionary (:issue:`2132`).
+* Fixed checking for *Bits Stored* when converting *Float Pixel Data* and *Double Float
+  Pixel Data* using the :mod:`~pydicom.pixels` backend (:issue:`2135`).
diff --git a/src/pydicom/data/hashes.json b/src/pydicom/data/hashes.json
index 958dc1832..45eed9002 100644
--- a/src/pydicom/data/hashes.json
+++ b/src/pydicom/data/hashes.json
@@ -74,5 +74,7 @@
   "JLSL_RGB_ILV2.dcm": "f8d670e9988cbca207d3367d916aff3cb508c076495211a0d132692266e9546d",
   "JLSN_RGB_ILV0.dcm": "a377750d24bd3413d21faa343662dfff997db9acf65c0b095c5d8a95beb866fa",
   "JLSL_08_07_0_1F.dcm": "308fb028c8fbdd1e9a93e731978ea4da6b15cb55b40451cf6f21e7c9ba35dd8a",
-  "JLSL_16_15_1_1F.dcm": "61f38f250a7dc82c44529c0face2eeab3ffd02ca8b9dfc756dd818eb252104b6"
+  "JLSL_16_15_1_1F.dcm": "61f38f250a7dc82c44529c0face2eeab3ffd02ca8b9dfc756dd818eb252104b6",
+  "parametric_map_float.dcm": "957f34397c26d82f7a90cad7a653ce0f7238f4be6aa9dfa9a33bae5dc2ce7e23",
+  "parametric_map_double_float.dcm": "a41e0b78b05e543a2448e22435858f9ca8d5f94807d7b391b93b4bca80e23a22"
 }
diff --git a/src/pydicom/data/urls.json b/src/pydicom/data/urls.json
index 548a8916c..62c61818e 100644
--- a/src/pydicom/data/urls.json
+++ b/src/pydicom/data/urls.json
@@ -74,5 +74,7 @@
     "JLSL_RGB_ILV2.dcm": "https://github.com/pydicom/pydicom-data/raw/7358d21f75fa8cdc8ae4e0bb02f7612c52a140ec/data_store/data/JLSL_RGB_ILV2.dcm",
     "JLSN_RGB_ILV0.dcm": "https://github.com/pydicom/pydicom-data/raw/7358d21f75fa8cdc8ae4e0bb02f7612c52a140ec/data_store/data/JLSN_RGB_ILV0.dcm",
     "JLSL_08_07_0_1F.dcm": "https://github.com/pydicom/pydicom-data/raw/831d25636b0aff13916975c493b20d6fadc01d2e/data_store/data/JLSL_08_07_0_1F.dcm",
-    "JLSL_16_15_1_1F.dcm": "https://github.com/pydicom/pydicom-data/raw/eaaec0d930f5e578db5e56892da57a8e63af71d7/data_store/data/JLSL_16_15_1_1F.dcm"
+    "JLSL_16_15_1_1F.dcm": "https://github.com/pydicom/pydicom-data/raw/eaaec0d930f5e578db5e56892da57a8e63af71d7/data_store/data/JLSL_16_15_1_1F.dcm",
+    "parametric_map_float.dcm": "https://github.com/pydicom/pydicom-data/raw/812f8edacbb6a1f3606ff4a9c16c54b831e9fd3b/data_store/data/parametric_map_float.dcm",
+    "parametric_map_double_float.dcm": "https://github.com/pydicom/pydicom-data/raw/812f8edacbb6a1f3606ff4a9c16c54b831e9fd3b/data_store/data/parametric_map_double_float.dcm"
 }
diff --git a/src/pydicom/pixels/common.py b/src/pydicom/pixels/common.py
index 89852aabe..737ebcf7f 100644
--- a/src/pydicom/pixels/common.py
+++ b/src/pydicom/pixels/common.py
@@ -572,15 +572,17 @@ class RunnerBase:
                 "is invalid, it must be 1 or a multiple of 8 and in the range (1, 64)"
             )
 
-        if self._opts.get("bits_stored") is None:
-            raise AttributeError(f"{prefix},0101) 'Bits Stored'")
+        if "Float" not in self.pixel_keyword:
+            if self._opts.get("bits_stored") is None:
+                raise AttributeError(f"{prefix},0101) 'Bits Stored'")
 
-        if not 1 <= self.bits_stored <= self.bits_allocated <= 64:
-            raise ValueError(
-                f"A (0028,0101) 'Bits Stored' value of '{self.bits_stored}' is "
-                "invalid, it must be in the range (1, 64) and no greater than "
-                f"the (0028,0100) 'Bits Allocated' value of '{self.bits_allocated}'"
-            )
+            if not 1 <= self.bits_stored <= self.bits_allocated <= 64:
+                raise ValueError(
+                    f"A (0028,0101) 'Bits Stored' value of '{self.bits_stored}' is "
+                    "invalid, it must be in the range (1, 64) and no greater than "
+                    "the (0028,0100) 'Bits Allocated' value of "
+                    f"'{self.bits_allocated}'"
+                )
 
         if self._opts.get("columns") is None:
             raise AttributeError(f"{prefix},0011) 'Columns'")
diff --git a/src/pydicom/pixels/decoders/base.py b/src/pydicom/pixels/decoders/base.py
index e3b3c7ef6..f75be168b 100644
--- a/src/pydicom/pixels/decoders/base.py
+++ b/src/pydicom/pixels/decoders/base.py
@@ -537,7 +537,6 @@ class DecodeRunner(RunnerBase):
         """
         d = {
             "bits_allocated": self.bits_allocated,
-            "bits_stored": self.bits_stored,
             "columns": self.columns,
             "number_of_frames": self.number_of_frames if not as_frame else 1,
             "photometric_interpretation": str(self.photometric_interpretation),
@@ -549,6 +548,7 @@ class DecodeRunner(RunnerBase):
             d["planar_configuration"] = self.planar_configuration
 
         if self.pixel_keyword == "PixelData":
+            d["bits_stored"] = self.bits_stored
             d["pixel_representation"] = self.pixel_representation
 
         return cast(dict[str, str | int], d)


Test patch:
diff --git a/tests/pixels/pixels_reference.py b/tests/pixels/pixels_reference.py
index 16f24e0bf..8291d2b63 100644
--- a/tests/pixels/pixels_reference.py
+++ b/tests/pixels/pixels_reference.py
@@ -604,6 +604,28 @@ def test(ref, arr, **kwargs):
 EXPL_32_3_2F = PixelReference("SC_rgb_32bit_2frame.dcm", "<u4", test)
 
 
+# EXPL, (32, NA), (1, 128, 128, 1), OD, MONOCHROME2
+# Float Pixel Data
+def test(ref, arr, **kwargs):
+    assert [0.92012787, 0.91510725, 0.9160201, 0.92104053] == [
+        round(x, 8) for x in arr[:4, 0]
+    ]
+
+
+EXPL_32_1F_FLOAT = PixelReference("parametric_map_float.dcm", "<f4", test)
+
+
+# EXPL, (64, NA), (1, 128, 128, 1), OD, MONOCHROME2
+# Double Float Pixel Data
+def test(ref, arr, **kwargs):
+    assert [0.9201278, 0.91510726, 0.91602008, 0.92104062] == [
+        round(x, 8) for x in arr[:4, 0]
+    ]
+
+
+EXPL_64_1F_DOUBLE_FLOAT = PixelReference("parametric_map_double_float.dcm", "<f8", test)
+
+
 PIXEL_REFERENCE[ExplicitVRLittleEndian] = [
     EXPL_1_1_1F,
     EXPL_1_1_3F,
@@ -622,6 +644,8 @@ PIXEL_REFERENCE[ExplicitVRLittleEndian] = [
     EXPL_16_3_2F,
     EXPL_32_3_1F,
     EXPL_32_3_2F,
+    EXPL_32_1F_FLOAT,  # Float Pixel Data
+    EXPL_64_1F_DOUBLE_FLOAT,  # Double Float Pixel Data
 ]
 PIXEL_REFERENCE[ImplicitVRLittleEndian] = [
     IMPL_08_08_3_0_1F_RGB,
diff --git a/tests/pixels/test_common.py b/tests/pixels/test_common.py
index da738d86f..103926eea 100644
--- a/tests/pixels/test_common.py
+++ b/tests/pixels/test_common.py
@@ -214,6 +214,7 @@ class TestRunnerBase:
         """Tests for validate_options()"""
         # Generic option validation
         runner = RunnerBase(ExplicitVRLittleEndian)
+        runner.set_option("pixel_keyword", "PixelData")
 
         msg = r"Missing required element: \(0028,0100\) 'Bits Allocated'"
         with pytest.raises(AttributeError, match=msg):
@@ -281,13 +282,15 @@ class TestRunnerBase:
         with pytest.raises(ValueError, match=msg):
             runner._validate_options()
 
+        runner.del_option("pixel_keyword")
+
         runner.set_option("photometric_interpretation", PI.RGB)
         msg = "No value for 'pixel_keyword' has been set"
         with pytest.raises(AttributeError, match=msg):
             runner._validate_options()
 
-        runner.set_option("pixel_keyword", -1)
-        msg = "Unknown 'pixel_keyword' value '-1'"
+        runner.set_option("pixel_keyword", "foo")
+        msg = "Unknown 'pixel_keyword' value 'foo'"
         with pytest.raises(ValueError, match=msg):
             runner._validate_options()
 
diff --git a/tests/pixels/test_decoder_base.py b/tests/pixels/test_decoder_base.py
index b53a29932..1d48d0040 100644
--- a/tests/pixels/test_decoder_base.py
+++ b/tests/pixels/test_decoder_base.py
@@ -356,6 +356,14 @@ class TestDecodeRunner:
         runner.set_option("extended_offsets", ([0], [10]))
         runner._validate_options()
 
+        # Float/Double Float Pixel Data
+        runner.del_option("bits_stored")
+        runner.set_option("pixel_keyword", "FloatPixelData")
+        runner._validate_options()
+
+        runner.set_option("pixel_keyword", "DoubleFloatPixelData")
+        runner._validate_options()
+
     @pytest.mark.skipif(not HAVE_NP, reason="Numpy is not available")
     def test_decode(self):
         """Test decode()"""
diff --git a/tests/pixels/test_decoder_native.py b/tests/pixels/test_decoder_native.py
index c731590b3..e43c523ad 100644
--- a/tests/pixels/test_decoder_native.py
+++ b/tests/pixels/test_decoder_native.py
@@ -25,13 +25,31 @@ try:
 except ImportError:
     HAVE_NP = False
 
-from .pixels_reference import PIXEL_REFERENCE, EXPL_16_1_1F_PAD, IMPL_32_1_1F
+from .pixels_reference import (
+    PIXEL_REFERENCE,
+    EXPL_16_1_1F_PAD,
+    IMPL_32_1_1F,
+    EXPL_64_1F_DOUBLE_FLOAT,
+)
 
 
 def name(ref):
     return f"{ref.name}"
 
 
+def get_pixel_keyword(ds):
+    if "PixelData" in ds:
+        return "PixelData"
+
+    if "FloatPixelData" in ds:
+        return "FloatPixelData"
+
+    if "DoubleFloatPixelData" in ds:
+        return "DoubleFloatPixelData"
+
+    raise ValueError("No pixel data element found")
+
+
 @pytest.mark.skipif(not HAVE_NP, reason="NumPy is not available")
 class TestAsArray:
     """Tests for decoder.as_array() with native transfer syntaxes"""
@@ -82,21 +100,25 @@ class TestAsArray:
         """Test against the reference data for explicit little for binary IO."""
         decoder = get_decoder(ExplicitVRLittleEndian)
         ds = reference.ds
+        pixel_keyword = get_pixel_keyword(ds)
+
         opts = {
             "rows": ds.Rows,
             "columns": ds.Columns,
             "samples_per_pixel": ds.SamplesPerPixel,
             "photometric_interpretation": ds.PhotometricInterpretation,
-            "pixel_representation": ds.PixelRepresentation,
             "bits_allocated": ds.BitsAllocated,
-            "bits_stored": ds.BitsStored,
             "number_of_frames": ds.get("NumberOfFrames", 1),
             "planar_configuration": ds.get("PlanarConfiguration", 0),
-            "pixel_keyword": "PixelData",
+            "pixel_keyword": pixel_keyword,
         }
 
+        if pixel_keyword == "PixelData":
+            opts["bits_stored"] = ds.BitsStored
+            opts["pixel_representation"] = ds.PixelRepresentation
+
         with open(reference.path, "rb") as f:
-            file_offset = reference.ds["PixelData"].file_tell
+            file_offset = reference.ds[pixel_keyword].file_tell
             f.seek(file_offset)
             arr, _ = decoder.as_array(f, raw=True, **opts)
             assert f.tell() == file_offset
@@ -229,35 +251,6 @@ class TestAsArray:
                 else:
                     assert arr.shape == reference.shape[1:]
 
-    def test_float_pixel_data(self):
-        """Test Float Pixel Data."""
-        # Only 1 sample per pixel allowed
-        ds = dcmread(IMPL_32_1_1F.path)
-        ds.FloatPixelData = ds.PixelData
-        del ds.PixelData
-        assert 32 == ds.BitsAllocated
-        decoder = get_decoder(ds.file_meta.TransferSyntaxUID)
-        arr, _ = decoder.as_array(ds, raw=True)
-        assert "float32" == arr.dtype
-
-        ref, _ = decoder.as_array(IMPL_32_1_1F.ds, raw=True)
-        assert np.array_equal(arr, ref.view("float32"))
-
-    def test_double_float_pixel_data(self):
-        """Test Double Float Pixel Data."""
-        # Only 1 sample per pixel allowed
-        ds = dcmread(IMPL_32_1_1F.path)
-        ds.DoubleFloatPixelData = ds.PixelData + ds.PixelData
-        del ds.PixelData
-        ds.BitsAllocated = 64
-        decoder = get_decoder(ds.file_meta.TransferSyntaxUID)
-        arr, _ = decoder.as_array(ds, raw=True)
-        assert "float64" == arr.dtype
-
-        ref, _ = decoder.as_array(IMPL_32_1_1F.ds, raw=True)
-        assert np.array_equal(arr.ravel()[:50], ref.view("float64").ravel())
-        assert np.array_equal(arr.ravel()[50:], ref.view("float64").ravel())
-
 
 @pytest.mark.skipif(not HAVE_NP, reason="NumPy is not available")
 class TestIterArray:
@@ -306,21 +299,25 @@ class TestIterArray:
         """Test against the reference data for explicit little for binary IO."""
         decoder = get_decoder(ExplicitVRLittleEndian)
         ds = reference.ds
+        pixel_keyword = get_pixel_keyword(ds)
+
         opts = {
             "rows": ds.Rows,
             "columns": ds.Columns,
             "samples_per_pixel": ds.SamplesPerPixel,
             "photometric_interpretation": ds.PhotometricInterpretation,
-            "pixel_representation": ds.PixelRepresentation,
             "bits_allocated": ds.BitsAllocated,
-            "bits_stored": ds.BitsStored,
             "number_of_frames": ds.get("NumberOfFrames", 1),
             "planar_configuration": ds.get("PlanarConfiguration", 0),
-            "pixel_keyword": "PixelData",
+            "pixel_keyword": pixel_keyword,
         }
 
+        if pixel_keyword == "PixelData":
+            opts["bits_stored"] = ds.BitsStored
+            opts["pixel_representation"] = ds.PixelRepresentation
+
         with open(reference.path, "rb") as f:
-            file_offset = reference.ds["PixelData"].file_tell
+            file_offset = reference.ds[pixel_keyword].file_tell
             f.seek(file_offset)
 
             frame_generator = decoder.iter_array(f, raw=True, **opts)
@@ -492,21 +489,25 @@ class TestAsBuffer:
             return
 
         ds = reference.ds
+        pixel_keyword = get_pixel_keyword(ds)
+
         opts = {
             "rows": ds.Rows,
             "columns": ds.Columns,
             "samples_per_pixel": ds.SamplesPerPixel,
             "photometric_interpretation": ds.PhotometricInterpretation,
-            "pixel_representation": ds.PixelRepresentation,
             "bits_allocated": ds.BitsAllocated,
-            "bits_stored": ds.BitsStored,
             "number_of_frames": ds.get("NumberOfFrames", 1),
             "planar_configuration": ds.get("PlanarConfiguration", 0),
-            "pixel_keyword": "PixelData",
+            "pixel_keyword": pixel_keyword,
         }
 
+        if pixel_keyword == "PixelData":
+            opts["bits_stored"] = ds.BitsStored
+            opts["pixel_representation"] = ds.PixelRepresentation
+
         with open(reference.path, "rb") as f:
-            file_offset = reference.ds["PixelData"].file_tell
+            file_offset = reference.ds[pixel_keyword].file_tell
             f.seek(file_offset)
             arr, _ = decoder.as_array(f, raw=True, **opts)
             buffer, _ = decoder.as_buffer(f, **opts)
@@ -705,28 +706,6 @@ class TestAsBuffer:
                     out[:27] = arr.ravel()
                     assert out.view(">u2").byteswap().tobytes() == buffer
 
-    def test_float_pixel_data(self):
-        """Test Float Pixel Data."""
-        ds = dcmread(IMPL_32_1_1F.path)
-        ref = ds.PixelData
-        ds.FloatPixelData = ref
-        del ds.PixelData
-        assert 32 == ds.BitsAllocated
-        decoder = get_decoder(ds.file_meta.TransferSyntaxUID)
-        buffer, _ = decoder.as_buffer(ds, raw=True)
-        assert buffer == ref
-
-    def test_double_float_pixel_data(self):
-        """Test Double Float Pixel Data."""
-        ds = dcmread(IMPL_32_1_1F.path)
-        ref = ds.PixelData + ds.PixelData
-        ds.DoubleFloatPixelData = ref
-        del ds.PixelData
-        ds.BitsAllocated = 64
-        decoder = get_decoder(ds.file_meta.TransferSyntaxUID)
-        buffer, _ = decoder.as_buffer(ds, raw=True)
-        assert buffer == ref
-
 
 @pytest.mark.skipif(not HAVE_NP, reason="NumPy is not available")
 class TestIterBuffer:
@@ -776,21 +755,25 @@ class TestIterBuffer:
             return
 
         ds = reference.ds
+        pixel_keyword = get_pixel_keyword(ds)
+
         opts = {
             "rows": ds.Rows,
             "columns": ds.Columns,
             "samples_per_pixel": ds.SamplesPerPixel,
             "photometric_interpretation": ds.PhotometricInterpretation,
-            "pixel_representation": ds.PixelRepresentation,
             "bits_allocated": ds.BitsAllocated,
-            "bits_stored": ds.BitsStored,
             "number_of_frames": ds.get("NumberOfFrames", 1),
             "planar_configuration": ds.get("PlanarConfiguration", 0),
-            "pixel_keyword": "PixelData",
+            "pixel_keyword": pixel_keyword,
         }
 
+        if pixel_keyword == "PixelData":
+            opts["bits_stored"] = ds.BitsStored
+            opts["pixel_representation"] = ds.PixelRepresentation
+
         with open(reference.path, "rb") as f:
-            file_offset = reference.ds["PixelData"].file_tell
+            file_offset = reference.ds[pixel_keyword].file_tell
             f.seek(file_offset)
             arr_gen = decoder.iter_array(f, raw=True, **opts)
             buf_gen = decoder.iter_buffer(f, **opts)