Siemens format DICOM with CSA header¶
Recent Siemens DICOM images have useful information stored in a private header. We’ll call this the CSA header.
CSA header¶
See this Siemens Syngo DICOM conformance statement, and a GDCM Siemens header dump.
The CSA header is stored in DICOM private tags. In the images we are looking at, there are several relevant tags:
(0029, 1008) [CSA Image Header Type] OB: 'IMAGE NUM 4 '
(0029, 1009) [CSA Image Header Version] OB: '20100114'
(0029, 1010) [CSA Image Header Info] OB: Array of 11560 bytes
(0029, 1018) [CSA Series Header Type] OB: 'MR'
(0029, 1019) [CSA Series Header Version] OB: '20100114'
(0029, 1020) [CSA Series Header Info] OB: Array of 80248 bytes
In our case we want to read the ‘CSAImageHeaderInfo’.
From the SPM (SPM8) code spm_dicom_headers.m
The CSAImageHeaderInfo and the CSA Series Header Info fields are of the same format. The fields can be of two types, CSA1 and CSA2.
Both are always little-endian, whatever the machine endian is.
The CSA2 format begins with the string ‘SV10’, the CSA1 format does not.
The code below keeps track of the position within the CSA header
stream. We’ll call this csa_position
. At this point (after
reading the 8 bytes of the header), csa_position == 8
. There’s a
variable that sets the last byte position in the file that is sensibly
still CSA header, and we’ll call that csa_max_pos
.
CSA1¶
Start header¶
n_tags, uint32, number of tags. Number of tags should apparently be between 1 and 128. If this is not true we just abort and move to
csa_max_pos
.unused, uint32, apparently has value 77
Each tag¶
name : S64, null terminated string 64 bytes
vm : int32
vr : S4, first 3 characters only
syngodt : int32
nitems : int32
xx : int32 - apparently either 77 or 205
nitems
gives the number of items in the tag. The items follow
directly after the tag.
Each item¶
xx : int32 * 4 . The first of these seems to be the length of the item in bytes, modified as below.
At this point SPM does a check, by calculating the length of this item
item_len
with xx[0]
- the nitems
of the first read tag.
If item_len
is less than 0 or greater than
csa_max_pos-csa_position
(the remaining number of bytes to read in
the whole header) then we break from the item reading loop,
setting the value below to ‘’.
Then we calculate item_len
rounded up to the nearest 4 byte boundary
tp get next_item_pos
.
value : uint8,
item_len
.
We set the stream position to next_item_pos
.
CSA2¶
Start header¶
hdr_id : S4 == ‘SV10’
unused1 : uint8, 4
n_tags, uint32, number of tags. Number of tags should apparently be between 1 and 128. If this is not true we just abort and move to
csa_max_pos
.unused2, uint32, apparently has value 77
Each tag¶
name : S64, null terminated string 64 bytes
vm : int32
vr : S4, first 3 characters only
syngodt : int32
nitems : int32
xx : int32 - apparently either 77 or 205
nitems
gives the number of items in the tag. The items follow
directly after the tag.
Each item¶
xx : int32 * 4 . The first of these seems to be the length of the item in bytes, modified as below.
Now there’s a different length check from CSA1. item_len
is given
just by xx[1]
. If item_len
> csa_max_pos - csa_position
(the remaining bytes in the header), then we just read the remaining
bytes in the header (as above) into value
below, as uint8, move the
filepointer to the next 4 byte boundary, and give up reading.
value : uint8,
item_len
.
We set the stream position to the next 4 byte boundary.