Repository: pydicom/pydicom

Issue:
Error writing values with VR OF
[Related to this comment](https://github.com/pydicom/pydicom/issues/452#issuecomment-614038937) (I think)

```python
from pydicom.dataset import Dataset
ds = Dataset()
ds.is_little_endian = True
ds.is_implicit_VR = True
ds.FloatPixelData = b'\x00\x00\x00\x00'
ds.save_as('out.dcm')
```
```
Traceback (most recent call last):
  File ".../pydicom/filewriter.py", line 228, in write_numbers
    value.append  # works only if list, not if string or number
AttributeError: 'bytes' object has no attribute 'append'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".../pydicom/filewriter.py", line 230, in write_numbers
    fp.write(pack(format_string, value))
struct.error: required argument is not a float

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".../pydicom/tag.py", line 27, in tag_in_exception
    yield
  File ".../pydicom/filewriter.py", line 543, in write_dataset
    write_data_element(fp, dataset.get_item(tag), dataset_encoding)
  File ".../pydicom/filewriter.py", line 472, in write_data_element
    writer_function(buffer, data_element, writer_param)
  File ".../pydicom/filewriter.py", line 236, in write_numbers
    "{0}\nfor data_element:\n{1}".format(str(e), str(data_element)))
OSError: required argument is not a float
for data_element:
(7fe0, 0008) Float Pixel Data                    OF: b'\x00\x00\x00\x00'

[skip]
```
[Error in filewriter](https://github.com/pydicom/pydicom/blob/master/pydicom/filewriter.py#L1007) using `write_numbers` instead of `write_OBvalue`/`write_OWvalue`. Looks like it's been wrong [since 2008](https://github.com/pydicom/pydicom/commit/5d3ea61ffe6877ae79267bf233f258c07c726998). I'm a bit surprised it hasn't come up before.


Discussion:

Patch:
diff --git a/pydicom/filewriter.py b/pydicom/filewriter.py
--- a/pydicom/filewriter.py
+++ b/pydicom/filewriter.py
@@ -1004,7 +1004,7 @@ def dcmwrite(filename, dataset, write_like_original=True):
     'LT': (write_text, None),
     'OB': (write_OBvalue, None),
     'OD': (write_OWvalue, None),
-    'OF': (write_numbers, 'f'),
+    'OF': (write_OWvalue, None),
     'OL': (write_OWvalue, None),
     'OW': (write_OWvalue, None),
     'OV': (write_OWvalue, None),
diff --git a/pydicom/values.py b/pydicom/values.py
--- a/pydicom/values.py
+++ b/pydicom/values.py
@@ -634,7 +634,7 @@ def convert_value(VR, raw_data_element, encodings=None):
     'LT': convert_single_string,
     'OB': convert_OBvalue,
     'OD': convert_OBvalue,
-    'OF': (convert_numbers, 'f'),
+    'OF': convert_OWvalue,
     'OL': convert_OBvalue,
     'OW': convert_OWvalue,
     'OV': convert_OVvalue,


Test patch:
diff --git a/pydicom/tests/test_filereader.py b/pydicom/tests/test_filereader.py
--- a/pydicom/tests/test_filereader.py
+++ b/pydicom/tests/test_filereader.py
@@ -700,6 +700,20 @@ def test_lut_descriptor_singleton(self):
         assert elem.value == -2816
         assert elem.VR == 'SS'
 
+    def test_reading_of(self):
+        """Test reading a dataset with OF element."""
+        bs = DicomBytesIO(
+            b'\x28\x00\x01\x11\x53\x53\x06\x00\x00\xf5\x00\xf8\x10\x00'
+            b'\xe0\x7f\x08\x00\x4F\x46\x00\x00\x04\x00\x00\x00\x00\x01\x02\x03'
+        )
+        bs.is_little_endian = True
+        bs.is_implicit_VR = False
+
+        ds = dcmread(bs, force=True)
+        elem = ds['FloatPixelData']
+        assert 'OF' == elem.VR
+        assert b'\x00\x01\x02\x03' == elem.value
+
 
 class TestIncorrectVR(object):
     def setup(self):
diff --git a/pydicom/tests/test_filewriter.py b/pydicom/tests/test_filewriter.py
--- a/pydicom/tests/test_filewriter.py
+++ b/pydicom/tests/test_filewriter.py
@@ -21,10 +21,11 @@
 from pydicom.dataelem import DataElement, RawDataElement
 from pydicom.filebase import DicomBytesIO
 from pydicom.filereader import dcmread, read_dataset, read_file
-from pydicom.filewriter import (write_data_element, write_dataset,
-                                correct_ambiguous_vr, write_file_meta_info,
-                                correct_ambiguous_vr_element, write_numbers,
-                                write_PN, _format_DT, write_text)
+from pydicom.filewriter import (
+    write_data_element, write_dataset, correct_ambiguous_vr,
+    write_file_meta_info, correct_ambiguous_vr_element, write_numbers,
+    write_PN, _format_DT, write_text, write_OWvalue
+)
 from pydicom.multival import MultiValue
 from pydicom.sequence import Sequence
 from pydicom.uid import (ImplicitVRLittleEndian, ExplicitVRBigEndian,
@@ -2251,6 +2252,32 @@ def test_write_big_endian(self):
         assert fp.getvalue() == b'\x00\x01'
 
 
+class TestWriteOtherVRs(object):
+    """Tests for writing the 'O' VRs like OB, OW, OF, etc."""
+    def test_write_of(self):
+        """Test writing element with VR OF"""
+        fp = DicomBytesIO()
+        fp.is_little_endian = True
+        elem = DataElement(0x7fe00008, 'OF', b'\x00\x01\x02\x03')
+        write_OWvalue(fp, elem)
+        assert fp.getvalue() == b'\x00\x01\x02\x03'
+
+    def test_write_of_dataset(self):
+        """Test writing a dataset with an element with VR OF."""
+        fp = DicomBytesIO()
+        fp.is_little_endian = True
+        fp.is_implicit_VR = False
+        ds = Dataset()
+        ds.is_little_endian = True
+        ds.is_implicit_VR = False
+        ds.FloatPixelData = b'\x00\x01\x02\x03'
+        ds.save_as(fp)
+        assert fp.getvalue() == (
+            # Tag             | VR            | Length        | Value
+            b'\xe0\x7f\x08\x00\x4F\x46\x00\x00\x04\x00\x00\x00\x00\x01\x02\x03'
+        )
+
+
 class TestWritePN(object):
     """Test filewriter.write_PN"""
 
diff --git a/pydicom/tests/test_values.py b/pydicom/tests/test_values.py
--- a/pydicom/tests/test_values.py
+++ b/pydicom/tests/test_values.py
@@ -5,9 +5,10 @@
 import pytest
 
 from pydicom.tag import Tag
-from pydicom.values import (convert_value, converters, convert_tag,
-                            convert_ATvalue, convert_DA_string, convert_text,
-                            convert_single_string, convert_AE_string)
+from pydicom.values import (
+    convert_value, converters, convert_tag, convert_ATvalue, convert_DA_string,
+    convert_text, convert_single_string, convert_AE_string
+)
 
 
 class TestConvertTag(object):
@@ -188,3 +189,11 @@ def test_convert_value_raises(self):
         # Fix converters
         converters['PN'] = converter_func
         assert 'PN' in converters
+
+
+class TestConvertOValues(object):
+    """Test converting values with the 'O' VRs like OB, OW, OF, etc."""
+    def test_convert_of(self):
+        """Test converting OF."""
+        fp = b'\x00\x01\x02\x03'
+        assert b'\x00\x01\x02\x03' == converters['OF'](fp, True)