Repository: pydicom/pydicom

Issue:
"underlying array is read-only" while modifying pixel value in pydicom version 1.1.0
<!-- Instructions For Filing a Bug: https://github.com/pydicom/pydicom/blob/master/CONTRIBUTING.md#filing-bugs -->
I tried to run a demo about reading dicom file, modify the pixel value and write new pixel data to another dicom file, however it failed in pydicom version 1.1.0 while works well for version 0.9.9-1.
In order to modify the pixel data in version 1.1.0 or later, what modification should I do?
Looking forward to your reply, thanks.

p.s. in order to run version 0.9.9-1, I just changed the module name from pydicom to dicom as recommended.
#### Description
<!-- Example: Attribute Error thrown when printing (0x0010, 0x0020) patient Id> 0-->

#### Steps/Code to Reproduce
<!--
Example:
```py
from io import BytesIO
from pydicom import dcmread

bytestream = b'\x02\x00\x02\x00\x55\x49\x16\x00\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31' \
             b'\x30\x30\x30\x38\x2e\x35\x2e\x31\x2e\x31\x2e\x39\x00\x02\x00\x10\x00' \
             b'\x55\x49\x12\x00\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38' \
             b'\x2e\x31\x2e\x32\x00\x20\x20\x10\x00\x02\x00\x00\x00\x01\x00\x20\x20' \
             b'\x20\x00\x06\x00\x00\x00\x4e\x4f\x52\x4d\x41\x4c'

fp = BytesIO(bytestream)
ds = dcmread(fp, force=True)

print(ds.PatientID)
```
If the code is too long, feel free to put it in a public gist and link
it in the issue: https://gist.github.com

When possible use pydicom testing examples to reproduce the errors. Otherwise, provide
an anonymous version of the data in order to replicate the errors.
-->
```
import pydicom
import pylab
ds = pydicom.dcmread("IMG.DCM")
 
##查看有哪些属性
print(ds.dir("pat"))
 
##查看对应属性的具体值
print(ds.PatientName)
 
##将属性值给某个元素
data_element = ds.data_element("PatientName")  # or data_element = ds[0x10,0x10]
print(data_element.VR, data_element.value)
 
##删除属性
#del ds.SoftwareVersions
 
##原始二进制文件
pixel_bytes = ds.PixelData
 
##CT值组成了一个矩阵
pix = ds.pixel_array
 
##读取显示图片
pylab.imshow(ds.pixel_array, cmap=pylab.cm.bone)
#pylab.show()
 
##修改图片中的元素，不能直接使用data_array,需要转换成PixelData
print('ds.pixel_array.shape =', ds.pixel_array.shape)
for n,val in enumerate(ds.pixel_array.flat): #example: zero anything < 300
    if val < 300:
        ds.pixel_array.flat[n] = 0
ds.PixelData = ds.pixel_array.tostring()
ds.save_as("IMG_NEW.DCM")
```
#### Expected Results
<!-- Please paste or describe the expected results.
Example: No error is thrown and the name of the patient is printed.-->
IMG_NEW.DCM is written successfully
#### Actual Results
<!-- Please paste or specifically describe the actual output or traceback.
(Use %xmode to deactivate ipython's trace beautifier)
Example: ```AttributeError: 'FileDataset' object has no attribute 'PatientID'```
-->
Traceback (most recent call last):
  File "readDicom.py", line 34, in <module>
    ds.pixel_array.flat[n] = 0
ValueError: underlying array is read-only

#### Versions
<!--
Please run the following snippet and paste the output below.
import platform; print(platform.platform())
import sys; print("Python", sys.version)
import pydicom; print("pydicom", pydicom.__version__)
-->
python 3.6.3
pydicom 1.1.0

<!-- Thanks for contributing! -->


Discussion:
pep8speaks: Hello @scaramallion! Thanks for submitting the PR.

 - In the file [`pydicom/tests/test_gdcm_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py), following                are the PEP8 issues :
> [Line 275:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L275): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 287:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L287): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 291:1](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L291): [E303](https://duckduckgo.com/?q=pep8%20E303) too many blank lines (3)
> [Line 337:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L337): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 349:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L349): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 364:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L364): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 404:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L404): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 421:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L421): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 594:30](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L594): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 645:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_gdcm_pixel_data.py#L645): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'


 - In the file [`pydicom/tests/test_jpeg_ls_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_jpeg_ls_pixel_data.py), following                are the PEP8 issues :
> [Line 202:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_jpeg_ls_pixel_data.py#L202): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 214:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_jpeg_ls_pixel_data.py#L214): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'


 - In the file [`pydicom/tests/test_numpy_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py), following                are the PEP8 issues :
> [Line 513:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L513): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 529:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L529): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 566:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L566): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 588:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L588): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 640:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L640): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 659:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L659): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 721:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L721): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 736:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L736): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 760:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L760): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 782:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L782): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 807:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L807): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 822:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L822): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 846:39](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L846): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'
> [Line 868:40](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_numpy_pixel_data.py#L868): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to False should be 'if cond is False:' or 'if not cond:'


 - In the file [`pydicom/tests/test_pillow_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_pillow_pixel_data.py), following                are the PEP8 issues :
> [Line 311:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_pillow_pixel_data.py#L311): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 320:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_pillow_pixel_data.py#L320): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 369:34](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_pillow_pixel_data.py#L369): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 567:30](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_pillow_pixel_data.py#L567): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'


 - In the file [`pydicom/tests/test_rle_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py), following                are the PEP8 issues :
> [Line 401:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L401): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 420:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L420): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 442:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L442): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 467:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L467): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 499:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L499): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 519:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L519): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 550:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L550): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 576:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L576): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 607:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L607): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 626:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L626): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 657:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L657): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'
> [Line 683:36](https://github.com/scaramallion/pydicom/blob/effde76c580ecb3cf2b449f7a4593c4d8a198149/pydicom/tests/test_rle_pixel_data.py#L683): [E712](https://duckduckgo.com/?q=pep8%20E712) comparison to True should be 'if cond is True:' or 'if cond:'



mrbean-bremen: If you are at it, you could also remove that unneeded `copy` at the end of the pillow handler (I introduced that unnessarily). The array can just be made writable before masking out the high bit.

Patch:
diff --git a/doc/whatsnew/v1.2.0.rst b/doc/whatsnew/v1.2.0.rst
index c130343b9..436b53c46 100644
--- a/doc/whatsnew/v1.2.0.rst
+++ b/doc/whatsnew/v1.2.0.rst
@@ -25,6 +25,7 @@ Changes
   handler's ``is_available`` method.
 * ``DeferredDataElement`` class deprecated and will be removed in v1.3
   (:issue:`291`)
+* The use of numpypy with PyPy is no longer supported, use numpy instead.
 
 
 Enhancements
@@ -54,9 +55,12 @@ Fixes
   (:pull_request:`660`)
 * Improve performance for Python 3 when dealing with compressed multi-frame
   Pixel Data with pillow and jpeg-ls (:issue:`682`).
-* Improve performance of bit unpacking for non-PyPy2 interpreters
-  (:pull_request:`715`)
+* Improve performance of bit unpacking (:pull_request:`715`)
 * First character set no longer removed (:issue:`707`)
 * Fixed RLE decoded data having the wrong byte order (:pull_request:`729`)
 * Fixed RLE decoded data having the wrong planar configuration
   (:pull_request:`729`)
+* Fixed numpy arrays returned by the pixel data handlers sometimes being
+  read-only. Read-only arrays are still available for uncompressed transfer
+  syntaxes via a keyword argument for the numpy pixel data handler and should
+  help reduce memory consumption if required. (:issue:`717`)
diff --git a/pydicom/multival.py b/pydicom/multival.py
index d992fa6e2..178a1565b 100644
--- a/pydicom/multival.py
+++ b/pydicom/multival.py
@@ -3,7 +3,10 @@
 or any list of items that must all be the same type.
 """
 
-from collections import MutableSequence
+try:
+    from collections.abc import MutableSequence
+except ImportError:
+    from collections import MutableSequence
 
 
 class MultiValue(MutableSequence):
diff --git a/pydicom/pixel_data_handlers/gdcm_handler.py b/pydicom/pixel_data_handlers/gdcm_handler.py
index fc747716c..153c64a94 100644
--- a/pydicom/pixel_data_handlers/gdcm_handler.py
+++ b/pydicom/pixel_data_handlers/gdcm_handler.py
@@ -46,7 +46,6 @@ def is_available():
     return HAVE_NP and HAVE_GDCM
 
 
-
 def needs_to_convert_to_RGB(dicom_dataset):
     should_convert = (dicom_dataset.file_meta.TransferSyntaxUID in
                       should_convert_these_syntaxes_to_RGB)
@@ -174,6 +173,7 @@ def get_pixeldata(dicom_dataset):
         n_bytes *= dicom_dataset.SamplesPerPixel
     except Exception:
         pass
+
     if len(pixel_bytearray) > n_bytes:
         # We make sure that all the bytes after are in fact zeros
         padding = pixel_bytearray[n_bytes:]
@@ -183,7 +183,9 @@ def get_pixeldata(dicom_dataset):
             # We revert to the old behavior which should then result
             #   in a Numpy error later on.
             pass
+
     pixel_array = numpy.frombuffer(pixel_bytearray, dtype=numpy_dtype)
+
     length_of_pixel_array = pixel_array.nbytes
     expected_length = dicom_dataset.Rows * dicom_dataset.Columns
 
@@ -192,10 +194,13 @@ def get_pixeldata(dicom_dataset):
 
     if dicom_dataset.BitsAllocated > 8:
         expected_length *= (dicom_dataset.BitsAllocated // 8)
+
     if length_of_pixel_array != expected_length:
         raise AttributeError("Amount of pixel data %d does "
                              "not match the expected data %d" %
                              (length_of_pixel_array, expected_length))
+
     if should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
         dicom_dataset.PhotometricInterpretation = "RGB"
-    return pixel_array
+
+    return pixel_array.copy()
diff --git a/pydicom/pixel_data_handlers/jpeg_ls_handler.py b/pydicom/pixel_data_handlers/jpeg_ls_handler.py
index 29c919dcc..e7285eddf 100644
--- a/pydicom/pixel_data_handlers/jpeg_ls_handler.py
+++ b/pydicom/pixel_data_handlers/jpeg_ls_handler.py
@@ -137,7 +137,7 @@ def get_pixeldata(dicom_dataset):
             dicom_dataset.PixelData)
         decompressed_image = jpeg_ls.decode(
             numpy.frombuffer(CompressedPixelData, dtype=numpy.uint8))
-        UncompressedPixelData = decompressed_image.tobytes()
+        UncompressedPixelData.extend(decompressed_image.tobytes())
 
     pixel_array = numpy.frombuffer(UncompressedPixelData, numpy_format)
     if should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
diff --git a/pydicom/pixel_data_handlers/numpy_handler.py b/pydicom/pixel_data_handlers/numpy_handler.py
index d60b5a9ce..8822f1df2 100644
--- a/pydicom/pixel_data_handlers/numpy_handler.py
+++ b/pydicom/pixel_data_handlers/numpy_handler.py
@@ -38,6 +38,7 @@ elements have values given in the table below.
 
 from platform import python_implementation
 from sys import byteorder
+import warnings
 
 try:
     import numpy as np
@@ -243,7 +244,7 @@ def unpack_bits(bytestream):
     return arr
 
 
-def get_pixeldata(ds):
+def get_pixeldata(ds, read_only=False):
     """Return an ndarray of the Pixel Data.
 
     Parameters
@@ -251,6 +252,12 @@ def get_pixeldata(ds):
     ds : dataset.Dataset
         The DICOM dataset containing an Image Pixel module and the Pixel Data
         to be converted.
+    read_only : bool, optional
+        If False (default) then returns a writeable array that no longer uses
+        the original memory. If True and the value of (0028,0100) *Bits
+        Allocated* > 1 then returns a read-only array that uses the original
+        memory buffer of the pixel data. If *Bits Allocated* = 1 then always
+        returns a writeable array.
 
     Returns
     -------
@@ -313,4 +320,7 @@ def get_pixeldata(ds):
     if should_change_PhotometricInterpretation_to_RGB(ds):
         ds.PhotometricInterpretation = "RGB"
 
+    if not read_only and ds.BitsAllocated > 1:
+        return arr.copy()
+
     return arr
diff --git a/pydicom/pixel_data_handlers/pillow_handler.py b/pydicom/pixel_data_handlers/pillow_handler.py
index ba78c1b71..b5e03963b 100644
--- a/pydicom/pixel_data_handlers/pillow_handler.py
+++ b/pydicom/pixel_data_handlers/pillow_handler.py
@@ -188,28 +188,32 @@ def get_pixeldata(dicom_dataset):
                 UncompressedPixelData.extend(decompressed_image.tobytes())
         else:
             # single compressed frame
-            UncompressedPixelData = pydicom.encaps.defragment_data(
+            pixel_data = pydicom.encaps.defragment_data(
                 dicom_dataset.PixelData)
-            UncompressedPixelData = generic_jpeg_file_header + \
-                UncompressedPixelData[frame_start_from:]
+            pixel_data = generic_jpeg_file_header + \
+                pixel_data[frame_start_from:]
             try:
-                fio = io.BytesIO(UncompressedPixelData)
+                fio = io.BytesIO(pixel_data)
                 decompressed_image = Image.open(fio)
             except IOError as e:
                 raise NotImplementedError(e.strerror)
-            UncompressedPixelData = decompressed_image.tobytes()
+            UncompressedPixelData.extend(decompressed_image.tobytes())
     except Exception:
         raise
+
     logger.debug(
-        "Successfully read %s pixel bytes",
-        len(UncompressedPixelData))
-    pixel_array = numpy.copy(
-        numpy.frombuffer(UncompressedPixelData, numpy_format))
+        "Successfully read %s pixel bytes", len(UncompressedPixelData)
+    )
+
+    pixel_array = numpy.frombuffer(UncompressedPixelData, numpy_format)
+
     if (transfer_syntax in
             PillowJPEG2000TransferSyntaxes and
             dicom_dataset.BitsStored == 16):
         # WHY IS THIS EVEN NECESSARY??
         pixel_array &= 0x7FFF
+
     if should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
         dicom_dataset.PhotometricInterpretation = "RGB"
+
     return pixel_array


Test patch:
diff --git a/pydicom/tests/test_gdcm_pixel_data.py b/pydicom/tests/test_gdcm_pixel_data.py
index 5c87f7e52..4d4c41fda 100644
--- a/pydicom/tests/test_gdcm_pixel_data.py
+++ b/pydicom/tests/test_gdcm_pixel_data.py
@@ -271,6 +271,8 @@ class GDCM_JPEG_LS_Tests_with_gdcm(unittest.TestCase):
             "using GDCM Decoded pixel data is not "
             "all {0} (mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
     def test_emri_JPEG_LS_PixelArray_with_gdcm(self):
         a = self.emri_jpeg_ls_lossless.pixel_array
         b = self.emri_small.pixel_array
@@ -280,6 +282,8 @@ class GDCM_JPEG_LS_Tests_with_gdcm(unittest.TestCase):
             "Decoded pixel data is not all {0} "
             "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
 
 @pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
@@ -326,6 +330,8 @@ class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
             "Decoded pixel data is not all {0} "
             "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
     def test_emri_JPEG2000PixelArray(self):
         a = self.emri_jpeg_2k_lossless.pixel_array
         b = self.emri_small.pixel_array
@@ -335,6 +341,8 @@ class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
             "Decoded pixel data is not all {0} "
             "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
     def test_jpeg2000_lossy(self):
         a = self.sc_rgb_jpeg2k_gdcm_KY.pixel_array
         b = self.ground_truth_sc_rgb_jpeg2k_gdcm_KY_gdcm.pixel_array
@@ -347,6 +355,8 @@ class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
                 "Decoded pixel data is not all {0} "
                 "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
 
 @pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEGlossyTests_with_gdcm(unittest.TestCase):
@@ -384,11 +394,16 @@ class GDCM_JPEGlossyTests_with_gdcm(unittest.TestCase):
         self.assertEqual(a[420, 140], 244)
         self.assertEqual(a[230, 120], 95)
 
+        assert a.flags.writeable
+
     def test_JPEGBaselineColor3DPixelArray(self):
         self.assertEqual(
             self.color_3d_jpeg.PhotometricInterpretation,
             "YBR_FULL_422")
         a = self.color_3d_jpeg.pixel_array
+
+        assert a.flags.writeable
+
         self.assertEqual(a.shape, (120, 480, 640, 3))
         a = _convert_YBR_FULL_to_RGB(a)
         # this test points were manually identified in Osirix viewer
@@ -567,6 +582,9 @@ def test_PI_RGB(test_with_gdcm,
     t = dcmread(image)
     assert t.PhotometricInterpretation == PhotometricInterpretation
     a = t.pixel_array
+
+    assert a.flags.writeable
+
     assert a.shape == (100, 100, 3)
     if convert_yuv_to_rgb:
         a = _convert_YBR_FULL_to_RGB(a)
@@ -614,3 +632,5 @@ class GDCM_JPEGlosslessTests_with_gdcm(unittest.TestCase):
         # this test points were manually identified in Osirix viewer
         self.assertEqual(a[420, 140], 227)
         self.assertEqual(a[230, 120], 105)
+
+        assert a.flags.writeable
diff --git a/pydicom/tests/test_jpeg_ls_pixel_data.py b/pydicom/tests/test_jpeg_ls_pixel_data.py
index 512acf1a5..e7f0a4eda 100644
--- a/pydicom/tests/test_jpeg_ls_pixel_data.py
+++ b/pydicom/tests/test_jpeg_ls_pixel_data.py
@@ -198,6 +198,8 @@ class jpeg_ls_JPEG_LS_Tests_with_jpeg_ls(unittest.TestCase):
             "Decoded pixel data is not all {0} "
             "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
     def test_emri_JPEG_LS_PixelArray(self):
         a = self.emri_jpeg_ls_lossless.pixel_array
         b = self.emri_small.pixel_array
@@ -207,6 +209,8 @@ class jpeg_ls_JPEG_LS_Tests_with_jpeg_ls(unittest.TestCase):
             "Decoded pixel data is not all {0} "
             "(mean == {1})".format(b.mean(), a.mean()))
 
+        assert a.flags.writeable
+
 
 @pytest.mark.skipif(
     not test_jpeg_ls_decoder,
diff --git a/pydicom/tests/test_numpy_pixel_data.py b/pydicom/tests/test_numpy_pixel_data.py
index 09bd11813..054f7133c 100644
--- a/pydicom/tests/test_numpy_pixel_data.py
+++ b/pydicom/tests/test_numpy_pixel_data.py
@@ -509,6 +509,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (600, 800) == arr.shape
             assert 244 == arr[0].min() == arr[0].max()
             assert (1, 246, 1) == tuple(arr[300, 491:494])
@@ -522,6 +524,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (2, 600, 800) == arr.shape
             # Frame 1
             assert 244 == arr[0, 0].min() == arr[0, 0].max()
@@ -556,6 +560,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (255, 0, 0) == tuple(arr[5, 50, :])
             assert (255, 128, 128) == tuple(arr[15, 50, :])
             assert (0, 255, 0) == tuple(arr[25, 50, :])
@@ -575,6 +581,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             # Frame 1
             frame = arr[0]
             assert (255, 0, 0) == tuple(frame[5, 50, :])
@@ -624,6 +632,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert arr.max() == 1
             assert arr.min() == 0
 
@@ -640,6 +650,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert arr.max() == 1
             assert arr.min() == 0
 
@@ -699,6 +711,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (422, 319, 361) == tuple(arr[0, 31:34])
             assert (366, 363, 322) == tuple(arr[31, :3])
             assert (1369, 1129, 862) == tuple(arr[-1, -3:])
@@ -711,6 +725,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             # Frame 1
             assert (206, 197, 159) == tuple(arr[0, 0, 31:34])
             assert (49, 78, 128) == tuple(arr[0, 31, :3])
@@ -732,6 +748,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (65535, 0, 0) == tuple(arr[5, 50, :])
             assert (65535, 32896, 32896) == tuple(arr[15, 50, :])
             assert (0, 65535, 0) == tuple(arr[25, 50, :])
@@ -751,6 +769,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             # Frame 1
             assert (65535, 0, 0) == tuple(arr[0, 5, 50, :])
             assert (65535, 32896, 32896) == tuple(arr[0, 15, 50, :])
@@ -773,6 +793,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             assert (1249000, 1249000, 1250000) == tuple(arr[0, :3])
             assert (1031000, 1029000, 1027000) == tuple(arr[4, 3:6])
             assert (803000, 801000, 798000) == tuple(arr[-1, -3:])
@@ -785,6 +807,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             # Frame 1
             assert (1249000, 1249000, 1250000) == tuple(arr[0, 0, :3])
             assert (1031000, 1029000, 1027000) == tuple(arr[0, 4, 3:6])
@@ -806,6 +830,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             ar = ds.pixel_array
 
+            assert ar.flags.writeable
+
             assert (4294967295, 0, 0) == tuple(ar[5, 50, :])
             assert (4294967295, 2155905152, 2155905152) == tuple(ar[15, 50, :])
             assert (0, 4294967295, 0) == tuple(ar[25, 50, :])
@@ -825,6 +851,8 @@ class TestNumpy_NumpyHandler(object):
             ds.file_meta.TransferSyntaxUID = uid
             arr = ds.pixel_array
 
+            assert arr.flags.writeable
+
             # Frame 1
             assert (4294967295, 0, 0) == tuple(arr[0, 5, 50, :])
             assert (4294967295, 2155905152, 2155905152) == tuple(
@@ -877,6 +905,15 @@ class TestNumpy_NumpyHandler(object):
 
         assert ds.pixel_array.max() == 1
 
+    def test_read_only(self):
+        """Test for #717, returned array read-only."""
+        ds = dcmread(EXPL_8_1_1F)
+        arr = ds.pixel_array
+        assert 0 != arr[0, 0]
+        arr[0, 0] = 0
+        assert 0 == arr[0, 0]
+        assert arr.flags.writeable
+
 
 # Tests for numpy_handler module with Numpy available
 @pytest.mark.skipif(not HAVE_NP, reason='Numpy is not available')
@@ -906,6 +943,24 @@ class TestNumpy_GetPixelData(object):
                            match=' the transfer syntax is not supported'):
             get_pixeldata(ds)
 
+    def test_bad_length_raises(self):
+        """Test bad pixel data length raises exception."""
+        ds = dcmread(EXPL_8_1_1F)
+        # 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."
+        )
+        with pytest.raises(ValueError, match=msg):
+            get_pixeldata(ds)
+
+        # Too long
+        ds.PixelData += b'\x00\x00'
+        with pytest.raises(ValueError, match=r"480001 vs. 480000 bytes"):
+            get_pixeldata(ds)
+
     def test_change_photometric_interpretation(self):
         """Test get_pixeldata changes PhotometricInterpretation if required."""
         def to_rgb(ds):
@@ -928,6 +983,29 @@ class TestNumpy_GetPixelData(object):
 
         NP_HANDLER.should_change_PhotometricInterpretation_to_RGB = orig_fn
 
+    def test_array_read_only(self):
+        """Test returning a read only array for BitsAllocated > 8."""
+        ds = dcmread(EXPL_8_1_1F)
+        arr = get_pixeldata(ds, read_only=False)
+        assert arr.flags.writeable
+        assert 0 != arr[10]
+        arr[10] = 0
+        assert 0 == arr[10]
+
+        arr = get_pixeldata(ds, read_only=True)
+        assert not arr.flags.writeable
+        with pytest.raises(ValueError, match="is read-only"):
+            arr[10] = 0
+
+    def test_array_read_only_bit_packed(self):
+        """Test returning a read only array for BitsAllocated = 1."""
+        ds = dcmread(EXPL_1_1_1F)
+        arr = get_pixeldata(ds, read_only=False)
+        assert arr.flags.writeable
+
+        arr = get_pixeldata(ds, read_only=True)
+        assert arr.flags.writeable
+
 
 REFERENCE_PACK_UNPACK = [
     (b'', []),
diff --git a/pydicom/tests/test_pillow_pixel_data.py b/pydicom/tests/test_pillow_pixel_data.py
index 25e39cff0..55514dfe0 100644
--- a/pydicom/tests/test_pillow_pixel_data.py
+++ b/pydicom/tests/test_pillow_pixel_data.py
@@ -307,12 +307,16 @@ class Test_JPEG2000Tests_with_pillow(object):
         b = self.mr_small.pixel_array
         assert np.array_equal(a, b)
 
+        assert a.flags.writeable
+
     def test_emri_JPEG2000PixelArray(self):
         """Test decoding JPEG2K with pillow handler succeeds."""
         a = self.emri_jpeg_2k_lossless.pixel_array
         b = self.emri_small.pixel_array
         assert np.array_equal(a, b)
 
+        assert a.flags.writeable
+
     def test_jpeg2000_lossy(self):
         """Test decoding JPEG2K lossy with pillow handler fails."""
         with pytest.raises(NotImplementedError):
@@ -358,6 +362,9 @@ class Test_JPEGlossyTests_with_pillow(object):
         assert "YBR_FULL_422" == self.color_3d_jpeg.PhotometricInterpretation
 
         a = self.color_3d_jpeg.pixel_array
+
+        assert a.flags.writeable
+
         assert (120, 480, 640, 3) == a.shape
         # this test points were manually identified in Osirix viewer
         assert (41, 41, 41) == tuple(a[3, 159, 290, :])
@@ -552,6 +559,8 @@ def test_PI_RGB(test_with_pillow,
     assert t.PhotometricInterpretation == PhotometricInterpretation
     a = t.pixel_array
     assert a.shape == (100, 100, 3)
+
+    assert a.flags.writeable
     """
     This complete test never gave a different result than
     just the 10 point test below
diff --git a/pydicom/tests/test_rle_pixel_data.py b/pydicom/tests/test_rle_pixel_data.py
index b94f0f430..266903930 100644
--- a/pydicom/tests/test_rle_pixel_data.py
+++ b/pydicom/tests/test_rle_pixel_data.py
@@ -397,6 +397,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(OB_EXPL_LITTLE_1F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (600, 800) == arr.shape
         assert 244 == arr[0].min() == arr[0].max()
@@ -413,6 +415,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(OB_EXPL_LITTLE_2F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (2, 600, 800) == arr.shape
         assert 244 == arr[0, 0].min() == arr[0, 0].max()
@@ -432,6 +436,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(SC_EXPL_LITTLE_1F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (255, 0, 0) == tuple(arr[5, 50, :])
         assert (255, 128, 128) == tuple(arr[15, 50, :])
@@ -454,6 +460,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(SC_EXPL_LITTLE_2F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
 
         # Frame 1
@@ -483,6 +491,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(MR_EXPL_LITTLE_1F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (64, 64) == arr.shape
 
@@ -500,6 +510,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(EMRI_EXPL_LITTLE_10F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (10, 64, 64) == arr.shape
 
@@ -528,6 +540,8 @@ class TestNumpy_RLEHandler(object):
         arr = ds.pixel_array
         ref = _get_pixel_array(SC_EXPL_LITTLE_16_1F)
 
+        assert arr.flags.writeable
+
         assert np.array_equal(ds.pixel_array, ref)
 
         assert (65535, 0, 0) == tuple(arr[5, 50, :])
@@ -551,6 +565,8 @@ class TestNumpy_RLEHandler(object):
         arr = ds.pixel_array
         ref = _get_pixel_array(SC_EXPL_LITTLE_16_2F)
 
+        assert arr.flags.writeable
+
         assert np.array_equal(ds.pixel_array, ref)
 
         # Frame 1
@@ -579,6 +595,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(RTDOSE_EXPL_LITTLE_1F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (10, 10) == arr.shape
         assert (1249000, 1249000, 1250000) == tuple(arr[0, :3])
@@ -595,6 +613,8 @@ class TestNumpy_RLEHandler(object):
         ref = _get_pixel_array(RTDOSE_EXPL_LITTLE_15F)
         arr = ds.pixel_array
 
+        assert arr.flags.writeable
+
         assert np.array_equal(arr, ref)
         assert (15, 10, 10) == arr.shape
 
@@ -623,6 +643,8 @@ class TestNumpy_RLEHandler(object):
         arr = ds.pixel_array
         ref = _get_pixel_array(SC_EXPL_LITTLE_32_1F)
 
+        assert arr.flags.writeable
+
         assert np.array_equal(ds.pixel_array, ref)
 
         assert (4294967295, 0, 0) == tuple(arr[5, 50, :])
@@ -646,6 +668,8 @@ class TestNumpy_RLEHandler(object):
         arr = ds.pixel_array
         ref = _get_pixel_array(SC_EXPL_LITTLE_32_2F)
 
+        assert arr.flags.writeable
+
         assert np.array_equal(ds.pixel_array, ref)
 
         # Frame 1