Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dcm_write bug #87

Open
AaronGhost opened this issue May 16, 2024 · 0 comments
Open

dcm_write bug #87

AaronGhost opened this issue May 16, 2024 · 0 comments

Comments

@AaronGhost
Copy link
Contributor

AaronGhost commented May 16, 2024

Thanks for writing this package, I have a couple of issues with the dcm_write function:

Invalid VR for tags accepting multiple VR

I have had some issues with writing back some DICOM image leading to the written image not being a valid DICOM anymore when a tag can be expressed with multiple vr, they are all written back instead of picking one.

For example, for (0x0040, 0x9211), the standard authorises either a VR of US or SS. In dcm_dict.jl, the data is set to:

(0x0040, 0x9211) => [:RealWorldValueLastValueMapped, "US or SS", "1"]

When writing, the VR is then set to "US or SS" overflowing on the length of the parameter. Further reads therefore try to read an incorrect amount of data, leading to an error.

Steps to reproduce:

using DICOM
empty_vr = Dict{Tuple{UInt16, UInt16}, String}();
io = IOBuffer();
DICOM.write_element(io, (0x0040, 0x9211), 0x0fff, true, empty_vr);
seek(io, 0);
read(io, String) # Show the overflow
seek(io, 0);
dcmdata = DICOM.DICOMData(Dict{Tuple{UInt16, UInt16}, Any}(), :little, true, empty_vr);
DICOM.read_element(io, dcmdata)

Result:

"@\0\x11\x92US or SS\x02\0\xff\x0f"
ERROR: EOFError: read end of file
Stacktrace:
 [1] peek
   @ Base .\iobuffer.jl:185 [inlined]
 [2] read(from::IOBuffer, T::Type{UInt16})
   @ Base .\iobuffer.jl:195
 [3] numeric_parse(st::IOBuffer, T::DataType, sz::UInt16, endian::Symbol)
   @ DICOM D:\Documents\Julia\DICOM.jl\src\DICOM.jl:351
 [4] read_element(st::IOBuffer, dcm::DICOM.DICOMData)
   @ DICOM D:\Documents\Julia\DICOM.jl\src\DICOM.jl:264
 [5] top-level scope
   @ REPL[88]:1

Passing an auxiliary VR when writing

When passing a custom VR value for a parameter, the auxiliary value will be ignored if the tag is in a sequence. PR #88 could solve this.

Trailing space in strings

Following: https://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html Values with VRs constructed of character strings, except in the case of the VR UI, shall be padded with SPACE characters (20H, in the Default Character Repertoire) when necessary to achieve even length.

When writing, the string values end up being padded with \0 instead of (space).**

using DICOM
empty_vr = Dict{Tuple{UInt16, UInt16}, String}();
io = IOBuffer();
DICOM.write_element(io, (0x0008, 0x1030), "BestImageEver", true, empty_vr);
seek(io, 0);
read(io, String)

Result (zero-padding):

"\b\x000\x10LO\x0e\0BestImageEver\0"

Writing sequences

The sequence must have (https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.2.html):

  • 4 bytes Data Element Length (following the VR if explicit VR is used) with Undefined Length or Exact Length
  • Item(s) with:
    • Item tag (4 bytes) = (FFFE, E000)
    • Item Length (4 bytes) either Undefined Length or Exact Length
    • Item Value Data Set
    • Item Delimiter tag (4 bytes) (FFFE, E00D) and Length (4 bytes) 0000 only if Item Length is Undefined Length
  • Sequence Delimitation Tag (4 bytes) (FFFE, E00D) and Length (4 bytes) only if Data Element Length is Undefined Length

When writing the Data Element Length is not written and the Item Length provides the Exact Length while adding an item delimiter tag.

using DICOM
# Setup internal SQ DICOMData
empty_vr = Dict{Tuple{UInt16, UInt16}, String}();
sq_meta = Dict{Tuple{UInt16, UInt16}, Any}([(0x0008, 0x0100) => "UNDEFINED", (0x0008, 0x010b) => 'N', (0x0008, 0x0104) => "UNDEFINED"]);
sq_el = DICOM.DICOMData(sq_meta, :little, true, empty_vr);

# Setup external SQ DICOMData
meta = Dict{Tuple{UInt16, UInt16}, Any}((0x0040, 0x0260) => [sq_el]);
el = DICOM.DICOMData(meta, :little, true, empty_vr)

# Write
io = IOBuffer();
DICOM.write_element(io, (0x0040, 0x0260), el[(0x0040, 0x0260)], true, empty_vr);
seek(io, 0);
read(io, String)

Result:

"@\0`\x02SQ\0\0F\0\0\0\xfe\xff\0\xe06\0\0\0\b\0\0\x01SH\n\0UNDEFINED\0\b\0\x04\x01LO\n\0UNDEFINED\0\b\0\v\x01CS\x02\0N\0\xfe\xff\r\xe0\0\0\0\0\xfe\xff\xdd\xe0\0\0\0\0"

@\0`\x02 => Tag
SQ => VR
\0\0 => VR Length
F\0\0\0 => Exact Length
\xfe\xff\0\xe0 => Item Tag
6\0\0\0 => Exact Item Length
\b\0\0\x01SH\n\0UNDEFINED\0 => (0x0008, 0x0100) VR Length "UNDEFINED" \0
\b\0\x04\x01LO\n\0UNDEFINED\0 => (0x0008, 0x0104) VR Length "UNDEFINED" \0
\b\0\v\x01CS\x02\0N\0 => (0x0008, 0x0104) VR Length "N" \0
\xfe\xff\r\xe0 => Item Delimiter Tag
\0\0\0\0 => Zero Length
\xfe\xff\xdd\xe => Sequence Delimiter Tag
0\0\0\0\0 => Zero Length

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant