-
-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathtag.go
233 lines (213 loc) · 5.92 KB
/
tag.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Package tag enumerates element tags defined in the DICOM standard.
//
// ftp://medical.nema.org/medical/dicom/2011/11_06pu.pdf
package tag
//go:generate ./generate_tag_definitions.py
//go:generate stringer -type VRKind
import (
"fmt"
"strconv"
"strings"
)
const (
// GroupSeqItem is the tag group for a sequence item.
GroupSeqItem = 0xFFFE
// UnknownVR indicates an unknown VR.
UnknownVR = "UN"
// VLUndefinedLength is the VL used to indicated undefined length.
VLUndefinedLength uint32 = 0xffffffff
)
// Tag is a <group, element> tuple that identifies an element type in a DICOM
// file. List of standard tags are defined in tag.go. See also:
//
// ftp://medical.nema.org/medical/dicom/2011/11_06pu.pdf
type Tag struct {
// Group and element are results of parsing the hex-pair tag, such as
// (1000,10008)
Group uint16
Element uint16
}
// Compare returns -1, 0, or 1 if t<other, t==other, t>other, respectively.
// Tags are ordered first by Group, then by Element.
func (t Tag) Compare(other Tag) int {
if t.Group < other.Group {
return -1
}
if t.Group > other.Group {
return 1
}
if t.Element < other.Element {
return -1
}
if t.Element > other.Element {
return 1
}
return 0
}
// Equals returns true if this tag equals the provided tag.
func (t Tag) Equals(other Tag) bool {
return t.Compare(other) == 0
}
// IsPrivate indicates if the input group is part of a private tag.
func IsPrivate(group uint16) bool {
return group%2 == 1
}
// String returns a string of form "(0008,1234)", where 0x0008 is t.Group,
// 0x1234 is t.Element.
func (t Tag) String() string {
return fmt.Sprintf("(%04x,%04x)", t.Group, t.Element)
}
// Tags represents a set of Tag structs.
type Tags []*Tag
// Contains returns true if the passed in tag exists within the Tags.
func (t Tags) Contains(item *Tag) bool {
for _, elem := range t {
if elem.Equals(*item) {
return true
}
}
return false
}
// Info stores detailed information about a Tag defined in the DICOM
// standard.
type Info struct {
Tag Tag
// Data encoding "UL", "CS", etc.
VR string
// Human-readable name of the tag, e.g., "CommandDataSetType"
Name string
// Cardinality (# of values expected in the element)
VM string
}
// MetadataGroup is the value of Tag.Group for metadata tags.
const MetadataGroup = 2
// VRKind defines the golang encoding of a VR.
type VRKind int
const (
// VRStringList means the element stores a list of strings
VRStringList VRKind = iota
// VRBytes means the element stores a []byte
VRBytes
// VRString means the element stores a string
VRString
// VRUInt16List means the element stores a list of uint16s
VRUInt16List
// VRUInt32List means the element stores a list of uint32s
VRUInt32List
// VRInt16List means the element stores a list of int16s
VRInt16List
// VRInt32List element stores a list of int32s
VRInt32List
// VRFloat32List element stores a list of float32s
VRFloat32List
// VRFloat64List element stores a list of float64s
VRFloat64List
// VRSequence means the element stores a list of *Elements, w/ TagItem
VRSequence
// VRItem means the element stores a list of *Elements
VRItem
// VRTagList element stores a list of Tags
VRTagList
// VRDate means the element stores a date string. Use ParseDate() to
// parse the date string.
VRDate
// VRPixelData means the element stores a PixelDataInfo
VRPixelData
)
// GetVRKind returns the golang value encoding of an element with <tag, vr>.
func GetVRKind(tag Tag, vr string) VRKind {
if tag == Item {
return VRItem
} else if tag == PixelData {
return VRPixelData
}
switch vr {
case "DA":
return VRDate
case "AT":
return VRTagList
case "OW", "OB", "UN":
return VRBytes
case "LT", "UT":
return VRString
case "UL":
return VRUInt32List
case "SL":
return VRInt32List
case "US":
return VRUInt16List
case "SS":
return VRInt16List
case "FL":
return VRFloat32List
case "FD":
return VRFloat64List
case "SQ":
return VRSequence
default:
return VRStringList
}
}
// Find finds information about the given tag. If the tag is not part of
// the DICOM standard, or is retired from the standard, it returns an error.
func Find(tag Tag) (Info, error) {
maybeInitTagDict()
entry, ok := tagDict[tag]
if !ok {
// (0000-u-ffff,0000) UL GenericGroupLength 1 GENERIC
if tag.Group%2 == 0 && tag.Element == 0x0000 {
entry = Info{tag, "UL", "GenericGroupLength", "1"}
} else {
return Info{}, fmt.Errorf("Could not find tag (0x%x, 0x%x) in dictionary", tag.Group, tag.Element)
}
}
return entry, nil
}
// MustFind is like FindTag, but panics on error.
func MustFind(tag Tag) Info {
e, err := Find(tag)
if err != nil {
panic(fmt.Sprintf("tag %v not found: %s", tag, err))
}
return e
}
// FindByName finds information about the tag with the given name. If the tag is
// not part of the DICOM standard, or is retired from the standard, it returns
// an error.
//
// Example: FindTagByName("TransferSyntaxUID")
func FindByName(name string) (Info, error) {
maybeInitTagDict()
for _, ent := range tagDict {
if ent.Name == name {
return ent, nil
}
}
return Info{}, fmt.Errorf("Could not find tag with name %s", name)
}
// DebugString returns a human-readable diagnostic string for the tag, in format
// "(group, elem)[name]".
func DebugString(tag Tag) string {
e, err := Find(tag)
if err != nil {
if IsPrivate(tag.Group) {
return fmt.Sprintf("(%04x,%04x)[private]", tag.Group, tag.Element)
}
return fmt.Sprintf("(%04x,%04x)[??]", tag.Group, tag.Element)
}
return fmt.Sprintf("(%04x,%04x)[%s]", tag.Group, tag.Element, e.Name)
}
// Split a tag into a group and element, represented as a hex value
// TODO: support group ranges (6000-60FF,0803)
func parseTag(tag string) (Tag, error) {
parts := strings.Split(strings.Trim(tag, "()"), ",")
group, err := strconv.ParseInt(parts[0], 16, 0)
if err != nil {
return Tag{}, err
}
elem, err := strconv.ParseInt(parts[1], 16, 0)
if err != nil {
return Tag{}, err
}
return Tag{Group: uint16(group), Element: uint16(elem)}, nil
}