Repository: pydicom/pydicom

Issue:
Handle odd-sized dicoms with warning
<!-- Instructions For Filing a Bug: https://github.com/pydicom/pydicom/blob/master/CONTRIBUTING.md#filing-bugs -->

#### Description
<!-- Example: Attribute Error thrown when printing (0x0010, 0x0020) patient Id> 0-->

We have some uncompressed dicoms with an odd number of pixel bytes (saved by older versions of pydicom actually). 

When we re-open with pydicom 1.2.2, we're now unable to extract the image, due to the change made by https://github.com/pydicom/pydicom/pull/601

Would it be possible to emit a warning instead of rejecting the dicom for such cases?

#### Version
1.2.2


Discussion:
I agree that this would be sensible. Checking the mentioned PR, I also found that we had [discussed this briefly](https://github.com/pydicom/pydicom/pull/601#issuecomment-374710953) (with no result, obviously).
@darcymason - I think this is another case of "try to handle broken DICOM, only raise in 'strict' mode", especially as these images have been written by pydicom itself...
unsponsored plug: I like reviewable.io to keep track of what comments are resolved/unresolved/just-a-nit before merging. Looks like its free for open source repos.
> @darcymason - I think this is another case of "try to handle broken DICOM, only raise in 'strict' mode", especially as these images have been written by pydicom itself...

Yes, I agree, for sure.  Even without it being written by pydicom, it is better to be tolerant on reading.

Patch:
diff --git a/pydicom/pixel_data_handlers/numpy_handler.py b/pydicom/pixel_data_handlers/numpy_handler.py
--- a/pydicom/pixel_data_handlers/numpy_handler.py
+++ b/pydicom/pixel_data_handlers/numpy_handler.py
@@ -242,14 +242,20 @@ def get_pixeldata(ds, read_only=False):
     # Check that the actual length of the pixel data is as expected
     actual_length = len(ds.PixelData)
     # Correct for the trailing NULL byte padding for odd length data
+
     padded_expected_len = expected_len + expected_len % 2
     if actual_length < padded_expected_len:
-        raise ValueError(
-            "The length of the pixel data in the dataset doesn't match the "
-            "expected amount ({0} vs. {1} bytes). The dataset may be "
-            "corrupted or there may be an issue with the pixel data handler."
-            .format(actual_length, padded_expected_len)
-        )
+        if actual_length == expected_len:
+            warnings.warn(
+                "The pixel data length is odd and misses a padding byte.")
+        else:
+            raise ValueError(
+                "The length of the pixel data in the dataset ({} bytes) "
+                "doesn't match the expected length ({} bytes). "
+                "The dataset may be corrupted or there may be an issue "
+                "with the pixel data handler."
+                .format(actual_length, padded_expected_len)
+            )
     elif actual_length > padded_expected_len:
         # PS 3.5, Section 8.1.1
         msg = (


Test patch:
diff --git a/pydicom/tests/test_numpy_pixel_data.py b/pydicom/tests/test_numpy_pixel_data.py
--- a/pydicom/tests/test_numpy_pixel_data.py
+++ b/pydicom/tests/test_numpy_pixel_data.py
@@ -986,13 +986,23 @@ def test_bad_length_raises(self):
         # Too short
         ds.PixelData = ds.PixelData[:-1]
         msg = (
-            r"The length of the pixel data in the dataset doesn't match the "
-            r"expected amount \(479999 vs. 480000 bytes\). The dataset may be "
-            r"corrupted or there may be an issue with the pixel data handler."
+            r"The length of the pixel data in the dataset \(479999 bytes\) "
+            r"doesn't match the expected length \(480000 bytes\). "
+            r"The dataset may be corrupted or there may be an issue "
+            r"with the pixel data handler."
         )
         with pytest.raises(ValueError, match=msg):
             get_pixeldata(ds)
 
+    def test_missing_padding_warns(self):
+        """A warning shall be issued if the padding for odd data is missing."""
+        ds = dcmread(EXPL_8_3_1F_ODD)
+        # remove the padding byte
+        ds.PixelData = ds.PixelData[:-1]
+        msg = "The pixel data length is odd and misses a padding byte."
+        with pytest.warns(UserWarning, match=msg):
+            get_pixeldata(ds)
+
     def test_change_photometric_interpretation(self):
         """Test get_pixeldata changes PhotometricInterpretation if required."""
         def to_rgb(ds):