dicom 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -161,6 +161,8 @@
161
161
  1.2.840.10008.5.1.4.1.1.13.1.1 X-Ray 3D Angiographic Image Storage SOP Class
162
162
  1.2.840.10008.5.1.4.1.1.13.1.2 X-Ray 3D Craniofacial Image Storage SOP Class
163
163
  1.2.840.10008.5.1.4.1.1.13.1.3 Breast Tomosynthesis Image Storage SOP Class
164
+ 1.2.840.10008.5.1.4.1.1.13.1.4 Breast Projection X-Ray Image Storage - For Presentation SOP Class
165
+ 1.2.840.10008.5.1.4.1.1.13.1.5 Breast Projection X-Ray Image Storage - For Processing SOP Class
164
166
  1.2.840.10008.5.1.4.1.1.14.1 Intravascular Optical Coherence Tomography Image Storage - For Presentation SOP Class
165
167
  1.2.840.10008.5.1.4.1.1.14.2 Intravascular Optical Coherence Tomography Image Storage - For Processing SOP Class
166
168
  1.2.840.10008.5.1.4.1.1.20 Nuclear Medicine Image Storage SOP Class
@@ -171,8 +173,8 @@
171
173
  1.2.840.10008.5.1.4.1.1.66.4 Segmentation Storage SOP Class
172
174
  1.2.840.10008.5.1.4.1.1.66.5 Surface Segmentation Storage SOP Class
173
175
  1.2.840.10008.5.1.4.1.1.67 Real World Value Mapping Storage SOP Class
174
- 1.2.840.10008.5.1.4.1.1.77.1 VL Image Storage - Trial (Retired)   R
175
- 1.2.840.10008.5.1.4.1.1.77.2 VL Multi-frame Image Storage - Trial (Retired)   R
176
+ 1.2.840.10008.5.1.4.1.1.77.1 VL Image Storage - Trial (Retired) SOP Class R
177
+ 1.2.840.10008.5.1.4.1.1.77.2 VL Multi-frame Image Storage - Trial (Retired) SOP Class R
176
178
  1.2.840.10008.5.1.4.1.1.77.1.1 VL Endoscopic Image Storage SOP Class
177
179
  1.2.840.10008.5.1.4.1.1.77.1.1.1 Video Endoscopic Image Storage SOP Class
178
180
  1.2.840.10008.5.1.4.1.1.77.1.2 VL Microscopic Image Storage SOP Class
@@ -1,64 +1,64 @@
1
- module DICOM
2
-
3
- # The ElementalParent mix-in module contains methods that are common among
4
- # the two elemental parent classes: Item & Sequence
5
- #
6
- module ElementalParent
7
-
8
- # Adds a child item to a Sequence (or Item in some cases where pixel data is encapsulated).
9
- #
10
- # If no existing Item is given, a new item will be created and added.
11
- #
12
- # @note Items are specified by index (starting at 0) instead of a tag string!
13
- #
14
- # @param [Item] item the Item instance to be added
15
- # @param [Hash] options the options used for adding the item
16
- # option options [Integer] :if specified, forces the item to be inserted at that specific index (Item number)
17
- # option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is added
18
- # * <tt>options</tt> -- A hash of parameters.
19
- # @example Add an empty Item to a specific Sequence
20
- # dcm["3006,0020"].add_item
21
- # @example Add an existing Item at the 2nd item position/index in the specific Sequence
22
- # dcm["3006,0020"].add_item(my_item, :index => 1)
23
- #
24
- def add_item(item=nil, options={})
25
- if item
26
- if item.is_a?(Item)
27
- if index = options[:index]
28
- # This Item will take a specific index, and all existing Items with index higher or equal to this number will have their index increased by one.
29
- # Check if index is valid (must be an existing index):
30
- if index >= 0
31
- # If the index value is larger than the max index present, we dont need to modify the existing items.
32
- if index < @tags.length
33
- @tags = @tags.create_key_gap_at(index)
34
- else
35
- # Set the index value one higher than the already existing max value:
36
- index = @tags.length
37
- end
38
- #,Add the new Item and set its index:
39
- @tags[index] = item
40
- item.index = index
41
- else
42
- raise ArgumentError, "The specified index (#{index}) is out of range (must be a positive integer)."
43
- end
44
- else
45
- # Add the existing Item to this Sequence:
46
- index = @tags.length
47
- @tags[index] = item
48
- # Let the Item know what index key it's got in it's parent's Hash:
49
- item.index = index
50
- end
51
- # Set ourself as this item's new parent:
52
- item.set_parent(self) unless options[:no_follow]
53
- else
54
- raise ArgumentError, "Expected Item, got #{item.class}"
55
- end
56
- else
57
- # Create an empty item with self as parent:
58
- item = Item.new(:parent => self)
59
- end
60
- end
61
-
62
- end
63
-
1
+ module DICOM
2
+
3
+ # The ElementalParent mix-in module contains methods that are common among
4
+ # the two elemental parent classes: Item & Sequence
5
+ #
6
+ module ElementalParent
7
+
8
+ # Adds a child item to a Sequence (or Item in some cases where pixel data is encapsulated).
9
+ #
10
+ # If no existing Item is given, a new item will be created and added.
11
+ #
12
+ # @note Items are specified by index (starting at 0) instead of a tag string!
13
+ #
14
+ # @param [Item] item the Item instance to be added
15
+ # @param [Hash] options the options used for adding the item
16
+ # option options [Integer] :if specified, forces the item to be inserted at that specific index (Item number)
17
+ # option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is added
18
+ # * <tt>options</tt> -- A hash of parameters.
19
+ # @example Add an empty Item to a specific Sequence
20
+ # dcm["3006,0020"].add_item
21
+ # @example Add an existing Item at the 2nd item position/index in the specific Sequence
22
+ # dcm["3006,0020"].add_item(my_item, :index => 1)
23
+ #
24
+ def add_item(item=nil, options={})
25
+ if item
26
+ if item.is_a?(Item)
27
+ if index = options[:index]
28
+ # This Item will take a specific index, and all existing Items with index higher or equal to this number will have their index increased by one.
29
+ # Check if index is valid (must be an existing index):
30
+ if index >= 0
31
+ # If the index value is larger than the max index present, we dont need to modify the existing items.
32
+ if index < @tags.length
33
+ @tags = @tags.create_key_gap_at(index)
34
+ else
35
+ # Set the index value one higher than the already existing max value:
36
+ index = @tags.length
37
+ end
38
+ #,Add the new Item and set its index:
39
+ @tags[index] = item
40
+ item.index = index
41
+ else
42
+ raise ArgumentError, "The specified index (#{index}) is out of range (must be a positive integer)."
43
+ end
44
+ else
45
+ # Add the existing Item to this Sequence:
46
+ index = @tags.length
47
+ @tags[index] = item
48
+ # Let the Item know what index key it's got in it's parent's Hash:
49
+ item.index = index
50
+ end
51
+ # Set ourself as this item's new parent:
52
+ item.set_parent(self) unless options[:no_follow]
53
+ else
54
+ raise ArgumentError, "Expected Item, got #{item.class}"
55
+ end
56
+ else
57
+ # Create an empty item with self as parent:
58
+ item = Item.new(:parent => self)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
64
  end
@@ -1,57 +1,57 @@
1
- # Extensions to the Array class.
2
- # These mainly deal with encoding integer arrays as well as conversion between
3
- # signed and unsigned integers.
4
- #
5
- class Array
6
-
7
- # Packs an array of (unsigned) integers to a binary string (blob).
8
- #
9
- # @param [Integer] depth the bit depth to be used when encoding the unsigned integers
10
- # @return [String] an encoded binary string
11
- #
12
- def to_blob(depth)
13
- raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
14
- raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
15
- case depth
16
- when 8
17
- return self.pack('C*') # Unsigned char
18
- when 16
19
- return self.pack('S<*') # Unsigned short, little endian byte order
20
- end
21
- end
22
-
23
- # Shifts the integer values of the array to make a signed data set.
24
- # The size of the shift is determined by the given bit depth.
25
- #
26
- # @param [Integer] depth the bit depth of the integers
27
- # @return [Array<Integer>] an array of signed integers
28
- #
29
- def to_signed(depth)
30
- case depth
31
- when 8
32
- self.collect {|i| i - 128}
33
- when 16
34
- self.collect {|i| i - 32768}
35
- else
36
- raise ArgumentError, "Unknown or unsupported bit depth: #{depth}"
37
- end
38
- end
39
-
40
- # Shifts the integer values of the array to make an unsigned data set.
41
- # The size of the shift is determined by the given bit depth.
42
- #
43
- # @param [Integer] depth the bit depth of the integers
44
- # @return [Array<Integer>] an array of unsigned integers
45
- #
46
- def to_unsigned(depth)
47
- case depth
48
- when 8
49
- self.collect {|i| i + 128}
50
- when 16
51
- self.collect {|i| i + 32768}
52
- else
53
- raise ArgumentError, "Unknown or unsupported bit depth: #{depth}"
54
- end
55
- end
56
-
1
+ # Extensions to the Array class.
2
+ # These mainly deal with encoding integer arrays as well as conversion between
3
+ # signed and unsigned integers.
4
+ #
5
+ class Array
6
+
7
+ # Packs an array of (unsigned) integers to a binary string (blob).
8
+ #
9
+ # @param [Integer] depth the bit depth to be used when encoding the unsigned integers
10
+ # @return [String] an encoded binary string
11
+ #
12
+ def to_blob(depth)
13
+ raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
14
+ raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
15
+ case depth
16
+ when 8
17
+ return self.pack('C*') # Unsigned char
18
+ when 16
19
+ return self.pack('S<*') # Unsigned short, little endian byte order
20
+ end
21
+ end
22
+
23
+ # Shifts the integer values of the array to make a signed data set.
24
+ # The size of the shift is determined by the given bit depth.
25
+ #
26
+ # @param [Integer] depth the bit depth of the integers
27
+ # @return [Array<Integer>] an array of signed integers
28
+ #
29
+ def to_signed(depth)
30
+ case depth
31
+ when 8
32
+ self.collect {|i| i - 128}
33
+ when 16
34
+ self.collect {|i| i - 32768}
35
+ else
36
+ raise ArgumentError, "Unknown or unsupported bit depth: #{depth}"
37
+ end
38
+ end
39
+
40
+ # Shifts the integer values of the array to make an unsigned data set.
41
+ # The size of the shift is determined by the given bit depth.
42
+ #
43
+ # @param [Integer] depth the bit depth of the integers
44
+ # @return [Array<Integer>] an array of unsigned integers
45
+ #
46
+ def to_unsigned(depth)
47
+ case depth
48
+ when 8
49
+ self.collect {|i| i + 128}
50
+ when 16
51
+ self.collect {|i| i + 32768}
52
+ else
53
+ raise ArgumentError, "Unknown or unsupported bit depth: #{depth}"
54
+ end
55
+ end
56
+
57
57
  end
@@ -1,31 +1,31 @@
1
- # Extensions to the Hash class used by the dicom gem.
2
- #
3
- class Hash
4
-
5
- # Creates a gap in the integer keys at the specified index.
6
- # This is achieved by incrementing by one all existing index keys that are equal or
7
- # bigger than the given index.
8
- #
9
- # @note It is assumed that this hash features integers as keys and items as values!
10
- # @param [Integer] index the index at which to clear
11
- # @return [Hash] the modified self
12
- #
13
- def create_key_gap_at(index)
14
- # Extract existing Hash entries to an array:
15
- pairs = self.sort
16
- h = Hash.new
17
- # Change the key of those equal or larger than index and put these key,value pairs back in a new Hash:
18
- pairs.each do |pair|
19
- if pair[0] < index
20
- # The key keeps its old index:
21
- h[pair[0]] = pair[1]
22
- else
23
- # The key (and the value Item) gets its index incremented by one:
24
- h[pair[0]+1] = pair[1]
25
- pair[1].index = pair[0]+1
26
- end
27
- end
28
- h
29
- end
30
-
1
+ # Extensions to the Hash class used by the dicom gem.
2
+ #
3
+ class Hash
4
+
5
+ # Creates a gap in the integer keys at the specified index.
6
+ # This is achieved by incrementing by one all existing index keys that are equal or
7
+ # bigger than the given index.
8
+ #
9
+ # @note It is assumed that this hash features integers as keys and items as values!
10
+ # @param [Integer] index the index at which to clear
11
+ # @return [Hash] the modified self
12
+ #
13
+ def create_key_gap_at(index)
14
+ # Extract existing Hash entries to an array:
15
+ pairs = self.sort
16
+ h = Hash.new
17
+ # Change the key of those equal or larger than index and put these key,value pairs back in a new Hash:
18
+ pairs.each do |pair|
19
+ if pair[0] < index
20
+ # The key keeps its old index:
21
+ h[pair[0]] = pair[1]
22
+ else
23
+ # The key (and the value Item) gets its index incremented by one:
24
+ h[pair[0]+1] = pair[1]
25
+ pair[1].index = pair[0]+1
26
+ end
27
+ end
28
+ h
29
+ end
30
+
31
31
  end
@@ -1,126 +1,126 @@
1
- # encoding: UTF-8
2
-
3
- # Extension to the String class. These mainly facilitate the processing and analysis of element tags.
4
- # A tag string (as used by the ruby-dicom library) is 9 characters long and of the form 'GGGG,EEEE'
5
- # (where G represents a group hexadecimal, and E represents an element hexadecimal).
6
- #
7
- class String
8
-
9
- # Checks if a string value LOOKS like a DICOM name - it may still not be valid one.
10
- #
11
- # @return [Boolean] true if a string looks like a DICOM name, and false if not
12
- #
13
- def dicom_name?
14
- self == self.dicom_titleize
15
- end
16
-
17
- # Checks if a string value LOOKS like a DICOM method name - it may still not be valid one.
18
- #
19
- # @return [Boolean] true if a string looks like a DICOM method name, and false if not
20
- #
21
- def dicom_method?
22
- self == self.dicom_underscore
23
- end
24
-
25
- # Capitalizes all the words in the string and replaces some characters to make a nicer looking title.
26
- #
27
- # @return [String] a formatted, capitalized string
28
- #
29
- def dicom_titleize
30
- self.dicom_underscore.gsub(/_/, ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
31
- end
32
-
33
- # Makes an underscored, lowercased version of the string.
34
- #
35
- # @return [String] an underscored, lower case string
36
- #
37
- def dicom_underscore
38
- word = self.dup
39
- word.tr!('-', '_')
40
- word.downcase!
41
- word
42
- end
43
-
44
- # Divides a string into a number of sub-strings of exactly equal length.
45
- #
46
- # @note The length of self must be a multiple of parts, or an exception will be raised.
47
- # @param [Integer] parts the number of sub-strings to create
48
- # @return [Array<String>] the divided sub-strings
49
- #
50
- def divide(parts)
51
- parts = parts.to_i
52
- raise ArgumentError, "Argument must be in the range <1 - self.length (#{self.length})>. Got #{parts}." if parts < 1 or parts > self.length
53
- raise ArgumentError, "Length of self (#{self.length}) must be a multiple of parts (#{parts})." if (self.length/parts.to_f % 1) != 0
54
- sub_strings = Array.new
55
- sub_length = self.length/parts
56
- start_index = 0
57
- parts.times {
58
- sub_strings << self.slice(start_index, sub_length)
59
- start_index += sub_length
60
- }
61
- sub_strings
62
- end
63
-
64
- # Extracts the element part of the tag string: The last 4 characters.
65
- #
66
- # @return [String] the element part of the tag
67
- #
68
- def element
69
- return self[5..8]
70
- end
71
-
72
- # Returns the group part of the tag string: The first 4 characters.
73
- #
74
- # @return [String] the group part of the tag
75
- #
76
- def group
77
- return self[0..3]
78
- end
79
-
80
- # Returns the "Group Length" ('GGGG,0000') tag which corresponds to the original tag/group string.
81
- # The original string may either be a 4 character group string, or a 9 character custom tag.
82
- #
83
- # @return [String] a group length tag
84
- #
85
- def group_length
86
- if self.length == 4
87
- return self + ',0000'
88
- else
89
- return self.group + ',0000'
90
- end
91
- end
92
-
93
- # Checks if the string is a "Group Length" tag (its element part is '0000').
94
- #
95
- # @return [Boolean] true if it is a group length tag, and false if not
96
- #
97
- def group_length?
98
- return (self.element == '0000' ? true : false)
99
- end
100
-
101
- # Checks if the string is a private tag (has an odd group number).
102
- #
103
- # @return [Boolean] true if it is a private tag, and false if not
104
- #
105
- def private?
106
- return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true)
107
- end
108
-
109
- # Checks if the string is a valid tag (as defined by ruby-dicom: 'GGGG,EEEE').
110
- #
111
- # @return [Boolean] true if it is a valid tag, and false if not
112
- #
113
- def tag?
114
- # Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
115
- return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true)
116
- end
117
-
118
- # Converts the string to a proper DICOM element method name symbol.
119
- #
120
- # @return [Symbol] a DICOM element method name
121
- #
122
- def to_element_method
123
- self.gsub(/^3/,'three_').gsub(/[#*?!]/,' ').gsub(', ',' ').gsub('&','and').gsub(' - ','_').gsub(' / ','_').gsub(/[\s\-\.\,\/\\]/,'_').gsub(/[\(\)\']/,'').gsub(/\_+/, '_').downcase.to_sym
124
- end
125
-
1
+ # encoding: UTF-8
2
+
3
+ # Extension to the String class. These mainly facilitate the processing and analysis of element tags.
4
+ # A tag string (as used by the ruby-dicom library) is 9 characters long and of the form 'GGGG,EEEE'
5
+ # (where G represents a group hexadecimal, and E represents an element hexadecimal).
6
+ #
7
+ class String
8
+
9
+ # Checks if a string value LOOKS like a DICOM name - it may still not be valid one.
10
+ #
11
+ # @return [Boolean] true if a string looks like a DICOM name, and false if not
12
+ #
13
+ def dicom_name?
14
+ self == self.dicom_titleize
15
+ end
16
+
17
+ # Checks if a string value LOOKS like a DICOM method name - it may still not be valid one.
18
+ #
19
+ # @return [Boolean] true if a string looks like a DICOM method name, and false if not
20
+ #
21
+ def dicom_method?
22
+ self == self.dicom_underscore
23
+ end
24
+
25
+ # Capitalizes all the words in the string and replaces some characters to make a nicer looking title.
26
+ #
27
+ # @return [String] a formatted, capitalized string
28
+ #
29
+ def dicom_titleize
30
+ self.dicom_underscore.gsub(/_/, ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
31
+ end
32
+
33
+ # Makes an underscored, lowercased version of the string.
34
+ #
35
+ # @return [String] an underscored, lower case string
36
+ #
37
+ def dicom_underscore
38
+ word = self.dup
39
+ word.tr!('-', '_')
40
+ word.downcase!
41
+ word
42
+ end
43
+
44
+ # Divides a string into a number of sub-strings of exactly equal length.
45
+ #
46
+ # @note The length of self must be a multiple of parts, or an exception will be raised.
47
+ # @param [Integer] parts the number of sub-strings to create
48
+ # @return [Array<String>] the divided sub-strings
49
+ #
50
+ def divide(parts)
51
+ parts = parts.to_i
52
+ raise ArgumentError, "Argument must be in the range <1 - self.length (#{self.length})>. Got #{parts}." if parts < 1 or parts > self.length
53
+ raise ArgumentError, "Length of self (#{self.length}) must be a multiple of parts (#{parts})." if (self.length/parts.to_f % 1) != 0
54
+ sub_strings = Array.new
55
+ sub_length = self.length/parts
56
+ start_index = 0
57
+ parts.times {
58
+ sub_strings << self.slice(start_index, sub_length)
59
+ start_index += sub_length
60
+ }
61
+ sub_strings
62
+ end
63
+
64
+ # Extracts the element part of the tag string: The last 4 characters.
65
+ #
66
+ # @return [String] the element part of the tag
67
+ #
68
+ def element
69
+ return self[5..8]
70
+ end
71
+
72
+ # Returns the group part of the tag string: The first 4 characters.
73
+ #
74
+ # @return [String] the group part of the tag
75
+ #
76
+ def group
77
+ return self[0..3]
78
+ end
79
+
80
+ # Returns the "Group Length" ('GGGG,0000') tag which corresponds to the original tag/group string.
81
+ # The original string may either be a 4 character group string, or a 9 character custom tag.
82
+ #
83
+ # @return [String] a group length tag
84
+ #
85
+ def group_length
86
+ if self.length == 4
87
+ return self + ',0000'
88
+ else
89
+ return self.group + ',0000'
90
+ end
91
+ end
92
+
93
+ # Checks if the string is a "Group Length" tag (its element part is '0000').
94
+ #
95
+ # @return [Boolean] true if it is a group length tag, and false if not
96
+ #
97
+ def group_length?
98
+ return (self.element == '0000' ? true : false)
99
+ end
100
+
101
+ # Checks if the string is a private tag (has an odd group number).
102
+ #
103
+ # @return [Boolean] true if it is a private tag, and false if not
104
+ #
105
+ def private?
106
+ return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true)
107
+ end
108
+
109
+ # Checks if the string is a valid tag (as defined by ruby-dicom: 'GGGG,EEEE').
110
+ #
111
+ # @return [Boolean] true if it is a valid tag, and false if not
112
+ #
113
+ def tag?
114
+ # Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
115
+ return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true)
116
+ end
117
+
118
+ # Converts the string to a proper DICOM element method name symbol.
119
+ #
120
+ # @return [Symbol] a DICOM element method name
121
+ #
122
+ def to_element_method
123
+ self.gsub(/^3/,'three_').gsub(/[#*?!]/,' ').gsub(', ',' ').gsub('&','and').gsub(' - ','_').gsub(' / ','_').gsub(/[\s\-\.\,\/\\]/,'_').gsub(/[\(\)\']/,'').gsub(/\_+/, '_').downcase.to_sym
124
+ end
125
+
126
126
  end