dicom 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +27 -27
- data/README.md +2 -1
- data/dicom.gemspec +8 -8
- data/lib/dicom/anonymizer.rb +3 -3
- data/lib/dicom/d_object.rb +2 -2
- data/lib/dicom/element.rb +278 -278
- data/lib/dicom/elemental.rb +121 -121
- data/lib/dicom/general/constants.rb +4 -1
- data/lib/dicom/general/version.rb +1 -1
- data/lib/dicom/image_item.rb +6 -6
- data/lib/dicom/item.rb +122 -122
- data/lib/dicom/link.rb +6 -6
- data/lib/dicom/sequence.rb +98 -98
- data/lib/dicom/stream.rb +461 -461
- metadata +23 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f91db23166e9a16c8cd6b4d694b7aa530e39b143
|
4
|
+
data.tar.gz: 76edf3567fb40346cc0ab81d2c6dced7efcdd556
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c39d93a981235ff3e63d80108d18c043ebd7ba12f161eb5791a5807ae297bb6f5a0f5b8aca364c6a0bdfd1e5036172553ebb3d2c0a609cddb43831ad0b92500a
|
7
|
+
data.tar.gz: 6be61b879bf566c73285f370da46241576ee3231d4b3e446c90512d7740c74564bcb6f312fa64dcb8a24df95ef43542736595a844ab6f34b95b0555e9272349e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# 0.9.8
|
2
|
+
|
3
|
+
## 24th March, 2018
|
4
|
+
|
5
|
+
* Changed: Replaced Fixnum with Integer to avoid deprecation warning in Ruby 2.4.
|
6
|
+
* Changed: Replaced NArray with Numo/Narray.
|
7
|
+
* Fixed: Improved compatibility with Japanese string encoding.
|
8
|
+
|
9
|
+
|
1
10
|
# 0.9.7
|
2
11
|
|
3
12
|
## 4th January, 2017
|
data/Gemfile.lock
CHANGED
@@ -1,49 +1,49 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dicom (0.9.
|
4
|
+
dicom (0.9.8)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://www.rubygems.org/
|
8
8
|
specs:
|
9
9
|
diff-lcs (1.2.5)
|
10
10
|
metaclass (0.0.4)
|
11
|
-
mini_magick (4.
|
12
|
-
mocha (1.
|
11
|
+
mini_magick (4.8.0)
|
12
|
+
mocha (1.4.0)
|
13
13
|
metaclass (~> 0.0.1)
|
14
|
-
narray (0.
|
15
|
-
rake (
|
16
|
-
redcarpet (3.
|
14
|
+
numo-narray (0.9.1.1)
|
15
|
+
rake (12.3.1)
|
16
|
+
redcarpet (3.4.0)
|
17
17
|
rmagick (2.15.4)
|
18
|
-
rspec (3.
|
19
|
-
rspec-core (~> 3.
|
20
|
-
rspec-expectations (~> 3.
|
21
|
-
rspec-mocks (~> 3.
|
22
|
-
rspec-core (3.
|
23
|
-
rspec-support (~> 3.
|
24
|
-
rspec-expectations (3.
|
18
|
+
rspec (3.7.0)
|
19
|
+
rspec-core (~> 3.7.0)
|
20
|
+
rspec-expectations (~> 3.7.0)
|
21
|
+
rspec-mocks (~> 3.7.0)
|
22
|
+
rspec-core (3.7.1)
|
23
|
+
rspec-support (~> 3.7.0)
|
24
|
+
rspec-expectations (3.7.0)
|
25
25
|
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.
|
27
|
-
rspec-mocks (3.
|
26
|
+
rspec-support (~> 3.7.0)
|
27
|
+
rspec-mocks (3.7.0)
|
28
28
|
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
-
rspec-support (~> 3.
|
30
|
-
rspec-support (3.
|
31
|
-
yard (0.
|
29
|
+
rspec-support (~> 3.7.0)
|
30
|
+
rspec-support (3.7.1)
|
31
|
+
yard (0.9.12)
|
32
32
|
|
33
33
|
PLATFORMS
|
34
34
|
x86-mingw32
|
35
35
|
|
36
36
|
DEPENDENCIES
|
37
|
-
bundler (~> 1.
|
37
|
+
bundler (~> 1.16)
|
38
38
|
dicom!
|
39
|
-
mini_magick (~> 4.
|
40
|
-
mocha (~> 1.
|
41
|
-
narray (~> 0.
|
42
|
-
rake (~>
|
43
|
-
redcarpet (~> 3.
|
39
|
+
mini_magick (~> 4.8)
|
40
|
+
mocha (~> 1.4)
|
41
|
+
numo-narray (~> 0.9, >= 0.9.1.1)
|
42
|
+
rake (~> 12.3)
|
43
|
+
redcarpet (~> 3.4)
|
44
44
|
rmagick (~> 2.15)
|
45
|
-
rspec (~> 3.
|
46
|
-
yard (~> 0.
|
45
|
+
rspec (~> 3.7)
|
46
|
+
yard (~> 0.9, >= 0.9.12)
|
47
47
|
|
48
48
|
BUNDLED WITH
|
49
|
-
1.
|
49
|
+
1.16.1
|
data/README.md
CHANGED
@@ -111,7 +111,7 @@ Example:
|
|
111
111
|
|
112
112
|
## COPYRIGHT
|
113
113
|
|
114
|
-
Copyright 2008-
|
114
|
+
Copyright 2008-2018 Christoffer Lervåg
|
115
115
|
|
116
116
|
This program is free software: you can redistribute it and/or modify
|
117
117
|
it under the terms of the GNU General Public License as published by
|
@@ -150,5 +150,6 @@ Please don't hesitate to email me if you have any feedback related to this proje
|
|
150
150
|
* [Steven Bedrick](https://github.com/stevenbedrick)
|
151
151
|
* [Lars Benner](https://github.com/Maturin)
|
152
152
|
* [Brett Goulder](https://github.com/brettgoulder)
|
153
|
+
* [Satoshi Funayama](https://github.com/akchan)
|
153
154
|
* André Wuttke
|
154
155
|
* Thomas Koschel
|
data/dicom.gemspec
CHANGED
@@ -19,13 +19,13 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.required_ruby_version = '>= 2.2.2'
|
21
21
|
|
22
|
-
s.add_development_dependency('bundler', '~> 1.
|
23
|
-
s.add_development_dependency('mini_magick', '~> 4.
|
24
|
-
s.add_development_dependency('mocha', '~> 1.
|
25
|
-
s.add_development_dependency('narray', '~> 0.
|
26
|
-
s.add_development_dependency('rake', '~>
|
27
|
-
s.add_development_dependency('redcarpet', '~> 3.
|
22
|
+
s.add_development_dependency('bundler', '~> 1.16')
|
23
|
+
s.add_development_dependency('mini_magick', '~> 4.8')
|
24
|
+
s.add_development_dependency('mocha', '~> 1.4')
|
25
|
+
s.add_development_dependency('numo-narray', '~> 0.9', '>= 0.9.1.1')
|
26
|
+
s.add_development_dependency('rake', '~> 12.3')
|
27
|
+
s.add_development_dependency('redcarpet', '~> 3.4')
|
28
28
|
s.add_development_dependency('rmagick', '~> 2.15')
|
29
|
-
s.add_development_dependency('rspec', '~> 3.
|
30
|
-
s.add_development_dependency('yard', '~> 0.
|
29
|
+
s.add_development_dependency('rspec', '~> 3.7')
|
30
|
+
s.add_development_dependency('yard', '~> 0.9', '>= 0.9.12')
|
31
31
|
end
|
data/lib/dicom/anonymizer.rb
CHANGED
@@ -50,7 +50,7 @@ module DICOM
|
|
50
50
|
# @option options [Boolean] :delete_private toggles whether private elements are to be deleted
|
51
51
|
# @option options [TrueClass, Digest::Class] :encryption if set as true, the default hash function (MD5) will be used for representing DICOM values in an audit file. Otherwise a Digest class can be given, e.g. Digest::SHA256
|
52
52
|
# @option options [Boolean] :enumeration toggles whether (some) elements get enumerated values (to enable post-anonymization re-identification)
|
53
|
-
# @option options [
|
53
|
+
# @option options [Integer] :logger_level the logger level which is applied to DObject operations during anonymization (defaults to Logger::FATAL)
|
54
54
|
# @option options [Boolean] :random_file_name toggles whether anonymized files will be given random file names when rewritten (in combination with the :write_path option)
|
55
55
|
# @option options [Boolean] :recursive toggles whether to anonymize on all sub-levels of the DICOM object tag hierarchies
|
56
56
|
# @option options [Boolean] :uid toggles whether UIDs will be replaced with custom generated UIDs (beware that to preserve UID relations in studies/series, the audit_trail feature must be used)
|
@@ -210,7 +210,7 @@ module DICOM
|
|
210
210
|
#
|
211
211
|
# @note Two objects with the same attributes will have the same hash code.
|
212
212
|
#
|
213
|
-
# @return [
|
213
|
+
# @return [Integer] the object's hash code
|
214
214
|
#
|
215
215
|
def hash
|
216
216
|
state.hash
|
@@ -471,7 +471,7 @@ module DICOM
|
|
471
471
|
# a new enumerated replacement value is found by increasing an index by 1.
|
472
472
|
#
|
473
473
|
# @param [String, Integer, Float] original the original value of the tag to be anonymized
|
474
|
-
# @param [
|
474
|
+
# @param [Integer] j the index of this tag in the tag-related instance arrays
|
475
475
|
# @return [String, Integer, Float] the replacement value which is used for the anonymization of the tag
|
476
476
|
#
|
477
477
|
def enumerated_value(original, j)
|
data/lib/dicom/d_object.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2008-
|
1
|
+
# Copyright 2008-2018 Christoffer Lervag
|
2
2
|
#
|
3
3
|
# This program is free software: you can redistribute it and/or modify
|
4
4
|
# it under the terms of the GNU General Public License as published by
|
@@ -238,7 +238,7 @@ module DICOM
|
|
238
238
|
#
|
239
239
|
# @note Two objects with the same attributes will have the same hash code.
|
240
240
|
#
|
241
|
-
# @return [
|
241
|
+
# @return [Integer] the object's hash code
|
242
242
|
#
|
243
243
|
def hash
|
244
244
|
state.hash
|
data/lib/dicom/element.rb
CHANGED
@@ -1,278 +1,278 @@
|
|
1
|
-
module DICOM
|
2
|
-
|
3
|
-
# The Element class handles information related to ordinary (non-parent) elementals (data elements).
|
4
|
-
#
|
5
|
-
class Element
|
6
|
-
|
7
|
-
# Include the Elemental mix-in module:
|
8
|
-
include Elemental
|
9
|
-
|
10
|
-
# Creates an Element instance.
|
11
|
-
#
|
12
|
-
# @note In the case where the Element is given a binary instead of value,
|
13
|
-
# the Element will not have a formatted value (value = nil).
|
14
|
-
# @note Private data elements are named as 'Private'.
|
15
|
-
# @note Non-private data elements that are not found in the dictionary are named as 'Unknown'.
|
16
|
-
#
|
17
|
-
# @param [String] tag a ruby-dicom type element tag string
|
18
|
-
# @param [String, Integer, Float, Array, NilClass] value a custom value to be encoded as the data element binary string, or in some cases (specified by options), a pre-encoded binary string
|
19
|
-
# @param [Hash] options the options to use for creating the element
|
20
|
-
#
|
21
|
-
# @option options [String] :bin if you already have the value pre-encoded to a binary string, the string can be supplied with this option to avoid it being encoded a second time
|
22
|
-
# @option options [Boolean] :encoded if the value parameter contains a pre-encoded binary, this boolean must to be set as true
|
23
|
-
# @option options [String] :name the name of the Element (if not specified, the name is retrieved from the dictionary)
|
24
|
-
# @option options [DObject, Item, NilClass] :parent a parent instance (Item or DObject) which the element belongs to
|
25
|
-
# @option options [String] :vr if a private element is created with a custom value, this must be specified to enable the encoding of the value (if not specified, the vr is retrieved from the dictionary)
|
26
|
-
#
|
27
|
-
# @example Create a new data element and connect it to a DObject instance
|
28
|
-
# patient_name = Element.new('0010,0010', 'John Doe', :parent => dcm)
|
29
|
-
# @example Create a "Pixel Data" element and insert image data that you have already encoded elsewhere
|
30
|
-
# pixel_data = Element.new('7FE0,0010', processed_pixel_data, :encoded => true, :parent => dcm)
|
31
|
-
# @example Create a private data element
|
32
|
-
# private = Element.new('0011,2102', some_data, :parent => dcm, :vr => 'LO')
|
33
|
-
#
|
34
|
-
def initialize(tag, value, options={})
|
35
|
-
raise ArgumentError, "The supplied tag (#{tag}) is not valid. The tag must be a string of the form 'GGGG,EEEE'." unless tag.is_a?(String) && tag.tag?
|
36
|
-
# Set instance variables:
|
37
|
-
@tag = tag.upcase
|
38
|
-
# We may need to retrieve name and vr from the library:
|
39
|
-
if options[:name] and options[:vr]
|
40
|
-
@name = options[:name]
|
41
|
-
@vr = options[:vr].upcase
|
42
|
-
else
|
43
|
-
name, vr = LIBRARY.name_and_vr(tag)
|
44
|
-
@name = options[:name] || name
|
45
|
-
@vr = (options[:vr] ? options[:vr].upcase : vr)
|
46
|
-
end
|
47
|
-
# Manage the parent relation if specified:
|
48
|
-
if options[:parent]
|
49
|
-
@parent = options[:parent]
|
50
|
-
# FIXME: Because of some implementation problems, attaching the special
|
51
|
-
# Data Set Trailing Padding element to a parent is not supported yet!
|
52
|
-
@parent.add(self, :no_follow => true) unless @tag == 'FFFC,FFFC' && @parent.is_a?(Sequence)
|
53
|
-
end
|
54
|
-
# Value may in some cases be the binary string:
|
55
|
-
unless options[:encoded]
|
56
|
-
# The Data Element may have a value, have no value and no binary, or have no value and only binary:
|
57
|
-
if value
|
58
|
-
# Is binary value provided or do we need to encode it?
|
59
|
-
if options[:bin]
|
60
|
-
@value = value
|
61
|
-
@bin = options[:bin]
|
62
|
-
else
|
63
|
-
if value == ''
|
64
|
-
@value = value
|
65
|
-
@bin = ''
|
66
|
-
else
|
67
|
-
# Set the value with our custom setter method to get proper encoding:
|
68
|
-
self.value = value
|
69
|
-
end
|
70
|
-
end
|
71
|
-
else
|
72
|
-
# When no value is present, we set the binary as an empty string, unless the binary is specified:
|
73
|
-
@bin = options[:bin] || ''
|
74
|
-
end
|
75
|
-
else
|
76
|
-
@bin = value
|
77
|
-
end
|
78
|
-
# Let the binary decide the length:
|
79
|
-
@length = @bin.length
|
80
|
-
end
|
81
|
-
|
82
|
-
# Checks for equality.
|
83
|
-
#
|
84
|
-
# Other and self are considered equivalent if they are
|
85
|
-
# of compatible types and their attributes are equivalent.
|
86
|
-
#
|
87
|
-
# @param other an object to be compared with self.
|
88
|
-
# @return [Boolean] true if self and other are considered equivalent
|
89
|
-
#
|
90
|
-
def ==(other)
|
91
|
-
if other.respond_to?(:to_element)
|
92
|
-
other.send(:state) == state
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
alias_method :eql?, :==
|
97
|
-
|
98
|
-
# Sets the binary string of a Element.
|
99
|
-
#
|
100
|
-
# @note if the specified binary has an odd length, a proper pad byte will automatically be appended
|
101
|
-
# to give it an even length (which is needed to conform with the DICOM standard).
|
102
|
-
#
|
103
|
-
# @param [String] new_bin a binary string of encoded data
|
104
|
-
#
|
105
|
-
def bin=(new_bin)
|
106
|
-
raise ArgumentError, "Expected String, got #{new_bin.class}." unless new_bin.is_a?(String)
|
107
|
-
# Add a zero byte at the end if the length of the binary is odd:
|
108
|
-
if new_bin.length.odd?
|
109
|
-
@bin = new_bin + stream.pad_byte[@vr]
|
110
|
-
else
|
111
|
-
@bin = new_bin
|
112
|
-
end
|
113
|
-
@value = nil
|
114
|
-
@length = @bin.length
|
115
|
-
end
|
116
|
-
|
117
|
-
# Checks if the Element actually has any child elementals.
|
118
|
-
#
|
119
|
-
# @return [FalseClass] always returns false, as Element instances by definition can't have children
|
120
|
-
#
|
121
|
-
def children?
|
122
|
-
return false
|
123
|
-
end
|
124
|
-
|
125
|
-
# Gives the endianness of the encoded binary value of this element.
|
126
|
-
#
|
127
|
-
# @return [Boolean] false if little endian, true if big endian
|
128
|
-
#
|
129
|
-
def endian
|
130
|
-
return stream.str_endian
|
131
|
-
end
|
132
|
-
|
133
|
-
# Computes a hash code for this object.
|
134
|
-
#
|
135
|
-
# @note Two objects with the same attributes will have the same hash code.
|
136
|
-
#
|
137
|
-
# @return [
|
138
|
-
#
|
139
|
-
def hash
|
140
|
-
state.hash
|
141
|
-
end
|
142
|
-
|
143
|
-
# Gives a string containing a human-readable hash representation of the Element.
|
144
|
-
#
|
145
|
-
# @return [String] a hash representation string of the element
|
146
|
-
#
|
147
|
-
def inspect
|
148
|
-
to_hash.inspect
|
149
|
-
end
|
150
|
-
|
151
|
-
# Checks if the Element is a parent.
|
152
|
-
#
|
153
|
-
# @return [FalseClass] always returns false, as Element instances by definition are not parents
|
154
|
-
#
|
155
|
-
def is_parent?
|
156
|
-
return false
|
157
|
-
end
|
158
|
-
|
159
|
-
# Creates a hash representation of the element instance.
|
160
|
-
#
|
161
|
-
# @note The key representation in this hash is configurable
|
162
|
-
# (refer to the DICOM module methods documentation for more details).
|
163
|
-
# @return [Hash] a hash containing a key & value pair (e.g. {"Modality"=>"MR"})
|
164
|
-
#
|
165
|
-
def to_hash
|
166
|
-
return {self.send(DICOM.key_representation) => value}
|
167
|
-
end
|
168
|
-
|
169
|
-
# Returns self.
|
170
|
-
#
|
171
|
-
# @return [Element] self
|
172
|
-
#
|
173
|
-
def to_element
|
174
|
-
self
|
175
|
-
end
|
176
|
-
|
177
|
-
# Gives a json string containing a human-readable representation of the Element.
|
178
|
-
#
|
179
|
-
# @return [String] a string containing a key & value pair (e.g. "{\"Modality\":\"MR\"}")
|
180
|
-
#
|
181
|
-
def to_json
|
182
|
-
to_hash.to_json
|
183
|
-
end
|
184
|
-
|
185
|
-
# Gives a yaml string containing a human-readable representation of the Element.
|
186
|
-
#
|
187
|
-
# @return [String] a string containing a key & value pair (e.g. "---\nModality: MR\n")
|
188
|
-
#
|
189
|
-
def to_yaml
|
190
|
-
to_hash.to_yaml
|
191
|
-
end
|
192
|
-
|
193
|
-
# Gives the (decoded) value of the data element.
|
194
|
-
#
|
195
|
-
# @note Returned string values are automatically converted from their originally
|
196
|
-
# encoding (e.g. ISO8859-1 or ASCII-8BIT) to UTF-8 for convenience reasons.
|
197
|
-
# If the value string is wanted in its original encoding, extract the data
|
198
|
-
# element's bin attribute instead.
|
199
|
-
#
|
200
|
-
# @note Note that according to the DICOM Standard PS 3.5 C.12.1.1.2, the Character Set only applies
|
201
|
-
# to values of data elements of type SH, LO, ST, PN, LT or UT. Currently in ruby-dicom, all
|
202
|
-
# string values are encoding converted regardless of VR, but whether this causes any problems is uknown.
|
203
|
-
#
|
204
|
-
# @return [String, Integer, Float] the formatted element value
|
205
|
-
#
|
206
|
-
def value
|
207
|
-
if @value.is_a?(String)
|
208
|
-
# Unless this is actually the Character Set data element,
|
209
|
-
# get the character set (note that it may not be available):
|
210
|
-
character_set = (@tag != '0008,0005' && top_parent.is_a?(DObject)) ? top_parent.value('0008,0005') : nil
|
211
|
-
# Convert to UTF-8 from [original encoding]:
|
212
|
-
# In most cases the original encoding is IS0-8859-1 (ISO_IR 100), but if
|
213
|
-
# it is not specified in the DICOM object, or if the specified string
|
214
|
-
# is not recognized, ASCII-8BIT is assumed.
|
215
|
-
@value.encode('UTF-8', ENCODING_NAME[character_set])
|
216
|
-
# If unpleasant encoding exceptions occur, the below version may be considered:
|
217
|
-
#@value.encode('UTF-8', ENCODING_NAME[character_set], :invalid => :replace, :undef => :replace)
|
218
|
-
else
|
219
|
-
@value
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Sets the value of the Element instance.
|
224
|
-
#
|
225
|
-
# In addition to updating the value attribute, the specified value is encoded to binary
|
226
|
-
# and used to update the Element's bin and length attributes too.
|
227
|
-
#
|
228
|
-
# @note The specified value must be of a type that is compatible with the Element's value representation (vr).
|
229
|
-
# @param [String, Integer, Float, Array] new_value a formatted value that is assigned to the element
|
230
|
-
#
|
231
|
-
def value=(new_value)
|
232
|
-
if VALUE_CONVERSION[@vr] == :to_s
|
233
|
-
# Unless this is actually the Character Set data element,
|
234
|
-
# get the character set (note that it may not be available):
|
235
|
-
character_set = (@tag != '0008,0005' && top_parent.is_a?(DObject)) ? top_parent.value('0008,0005') : nil
|
236
|
-
# Convert to [DObject encoding] from [input string encoding]:
|
237
|
-
# In most cases the DObject encoding is IS0-8859-1 (ISO_IR 100), but if
|
238
|
-
# it is not specified in the DICOM object, or if the specified string
|
239
|
-
# is not recognized, ASCII-8BIT is assumed.
|
240
|
-
@value = new_value.to_s.encode(ENCODING_NAME[character_set], new_value.to_s.encoding.name)
|
241
|
-
@bin = encode(@value)
|
242
|
-
else
|
243
|
-
# We may have an array (of numbers) which needs to be passed directly to
|
244
|
-
# the encode method instead of being forced into a numerical:
|
245
|
-
if new_value.is_a?(Array)
|
246
|
-
@value = new_value
|
247
|
-
@bin = encode(@value)
|
248
|
-
else
|
249
|
-
@value = new_value.send(VALUE_CONVERSION[@vr])
|
250
|
-
@bin = encode(@value)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
@length = @bin.length
|
254
|
-
end
|
255
|
-
|
256
|
-
|
257
|
-
private
|
258
|
-
|
259
|
-
|
260
|
-
# Encodes a formatted value to a binary string.
|
261
|
-
#
|
262
|
-
# @param [String, Integer, Float, Array] formatted_value a formatted value
|
263
|
-
# @return [String] the encoded, binary string
|
264
|
-
#
|
265
|
-
def encode(formatted_value)
|
266
|
-
stream.encode_value(formatted_value, @vr)
|
267
|
-
end
|
268
|
-
|
269
|
-
# Collects the attributes of this instance.
|
270
|
-
#
|
271
|
-
# @return [Array<String>] an array of attributes
|
272
|
-
#
|
273
|
-
def state
|
274
|
-
[@tag, @vr, @value, @bin]
|
275
|
-
end
|
276
|
-
|
277
|
-
end
|
278
|
-
end
|
1
|
+
module DICOM
|
2
|
+
|
3
|
+
# The Element class handles information related to ordinary (non-parent) elementals (data elements).
|
4
|
+
#
|
5
|
+
class Element
|
6
|
+
|
7
|
+
# Include the Elemental mix-in module:
|
8
|
+
include Elemental
|
9
|
+
|
10
|
+
# Creates an Element instance.
|
11
|
+
#
|
12
|
+
# @note In the case where the Element is given a binary instead of value,
|
13
|
+
# the Element will not have a formatted value (value = nil).
|
14
|
+
# @note Private data elements are named as 'Private'.
|
15
|
+
# @note Non-private data elements that are not found in the dictionary are named as 'Unknown'.
|
16
|
+
#
|
17
|
+
# @param [String] tag a ruby-dicom type element tag string
|
18
|
+
# @param [String, Integer, Float, Array, NilClass] value a custom value to be encoded as the data element binary string, or in some cases (specified by options), a pre-encoded binary string
|
19
|
+
# @param [Hash] options the options to use for creating the element
|
20
|
+
#
|
21
|
+
# @option options [String] :bin if you already have the value pre-encoded to a binary string, the string can be supplied with this option to avoid it being encoded a second time
|
22
|
+
# @option options [Boolean] :encoded if the value parameter contains a pre-encoded binary, this boolean must to be set as true
|
23
|
+
# @option options [String] :name the name of the Element (if not specified, the name is retrieved from the dictionary)
|
24
|
+
# @option options [DObject, Item, NilClass] :parent a parent instance (Item or DObject) which the element belongs to
|
25
|
+
# @option options [String] :vr if a private element is created with a custom value, this must be specified to enable the encoding of the value (if not specified, the vr is retrieved from the dictionary)
|
26
|
+
#
|
27
|
+
# @example Create a new data element and connect it to a DObject instance
|
28
|
+
# patient_name = Element.new('0010,0010', 'John Doe', :parent => dcm)
|
29
|
+
# @example Create a "Pixel Data" element and insert image data that you have already encoded elsewhere
|
30
|
+
# pixel_data = Element.new('7FE0,0010', processed_pixel_data, :encoded => true, :parent => dcm)
|
31
|
+
# @example Create a private data element
|
32
|
+
# private = Element.new('0011,2102', some_data, :parent => dcm, :vr => 'LO')
|
33
|
+
#
|
34
|
+
def initialize(tag, value, options={})
|
35
|
+
raise ArgumentError, "The supplied tag (#{tag}) is not valid. The tag must be a string of the form 'GGGG,EEEE'." unless tag.is_a?(String) && tag.tag?
|
36
|
+
# Set instance variables:
|
37
|
+
@tag = tag.upcase
|
38
|
+
# We may need to retrieve name and vr from the library:
|
39
|
+
if options[:name] and options[:vr]
|
40
|
+
@name = options[:name]
|
41
|
+
@vr = options[:vr].upcase
|
42
|
+
else
|
43
|
+
name, vr = LIBRARY.name_and_vr(tag)
|
44
|
+
@name = options[:name] || name
|
45
|
+
@vr = (options[:vr] ? options[:vr].upcase : vr)
|
46
|
+
end
|
47
|
+
# Manage the parent relation if specified:
|
48
|
+
if options[:parent]
|
49
|
+
@parent = options[:parent]
|
50
|
+
# FIXME: Because of some implementation problems, attaching the special
|
51
|
+
# Data Set Trailing Padding element to a parent is not supported yet!
|
52
|
+
@parent.add(self, :no_follow => true) unless @tag == 'FFFC,FFFC' && @parent.is_a?(Sequence)
|
53
|
+
end
|
54
|
+
# Value may in some cases be the binary string:
|
55
|
+
unless options[:encoded]
|
56
|
+
# The Data Element may have a value, have no value and no binary, or have no value and only binary:
|
57
|
+
if value
|
58
|
+
# Is binary value provided or do we need to encode it?
|
59
|
+
if options[:bin]
|
60
|
+
@value = value
|
61
|
+
@bin = options[:bin]
|
62
|
+
else
|
63
|
+
if value == ''
|
64
|
+
@value = value
|
65
|
+
@bin = ''
|
66
|
+
else
|
67
|
+
# Set the value with our custom setter method to get proper encoding:
|
68
|
+
self.value = value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# When no value is present, we set the binary as an empty string, unless the binary is specified:
|
73
|
+
@bin = options[:bin] || ''
|
74
|
+
end
|
75
|
+
else
|
76
|
+
@bin = value
|
77
|
+
end
|
78
|
+
# Let the binary decide the length:
|
79
|
+
@length = @bin.length
|
80
|
+
end
|
81
|
+
|
82
|
+
# Checks for equality.
|
83
|
+
#
|
84
|
+
# Other and self are considered equivalent if they are
|
85
|
+
# of compatible types and their attributes are equivalent.
|
86
|
+
#
|
87
|
+
# @param other an object to be compared with self.
|
88
|
+
# @return [Boolean] true if self and other are considered equivalent
|
89
|
+
#
|
90
|
+
def ==(other)
|
91
|
+
if other.respond_to?(:to_element)
|
92
|
+
other.send(:state) == state
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
alias_method :eql?, :==
|
97
|
+
|
98
|
+
# Sets the binary string of a Element.
|
99
|
+
#
|
100
|
+
# @note if the specified binary has an odd length, a proper pad byte will automatically be appended
|
101
|
+
# to give it an even length (which is needed to conform with the DICOM standard).
|
102
|
+
#
|
103
|
+
# @param [String] new_bin a binary string of encoded data
|
104
|
+
#
|
105
|
+
def bin=(new_bin)
|
106
|
+
raise ArgumentError, "Expected String, got #{new_bin.class}." unless new_bin.is_a?(String)
|
107
|
+
# Add a zero byte at the end if the length of the binary is odd:
|
108
|
+
if new_bin.length.odd?
|
109
|
+
@bin = new_bin + stream.pad_byte[@vr]
|
110
|
+
else
|
111
|
+
@bin = new_bin
|
112
|
+
end
|
113
|
+
@value = nil
|
114
|
+
@length = @bin.length
|
115
|
+
end
|
116
|
+
|
117
|
+
# Checks if the Element actually has any child elementals.
|
118
|
+
#
|
119
|
+
# @return [FalseClass] always returns false, as Element instances by definition can't have children
|
120
|
+
#
|
121
|
+
def children?
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
|
125
|
+
# Gives the endianness of the encoded binary value of this element.
|
126
|
+
#
|
127
|
+
# @return [Boolean] false if little endian, true if big endian
|
128
|
+
#
|
129
|
+
def endian
|
130
|
+
return stream.str_endian
|
131
|
+
end
|
132
|
+
|
133
|
+
# Computes a hash code for this object.
|
134
|
+
#
|
135
|
+
# @note Two objects with the same attributes will have the same hash code.
|
136
|
+
#
|
137
|
+
# @return [Integer] the object's hash code
|
138
|
+
#
|
139
|
+
def hash
|
140
|
+
state.hash
|
141
|
+
end
|
142
|
+
|
143
|
+
# Gives a string containing a human-readable hash representation of the Element.
|
144
|
+
#
|
145
|
+
# @return [String] a hash representation string of the element
|
146
|
+
#
|
147
|
+
def inspect
|
148
|
+
to_hash.inspect
|
149
|
+
end
|
150
|
+
|
151
|
+
# Checks if the Element is a parent.
|
152
|
+
#
|
153
|
+
# @return [FalseClass] always returns false, as Element instances by definition are not parents
|
154
|
+
#
|
155
|
+
def is_parent?
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
|
159
|
+
# Creates a hash representation of the element instance.
|
160
|
+
#
|
161
|
+
# @note The key representation in this hash is configurable
|
162
|
+
# (refer to the DICOM module methods documentation for more details).
|
163
|
+
# @return [Hash] a hash containing a key & value pair (e.g. {"Modality"=>"MR"})
|
164
|
+
#
|
165
|
+
def to_hash
|
166
|
+
return {self.send(DICOM.key_representation) => value}
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns self.
|
170
|
+
#
|
171
|
+
# @return [Element] self
|
172
|
+
#
|
173
|
+
def to_element
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
# Gives a json string containing a human-readable representation of the Element.
|
178
|
+
#
|
179
|
+
# @return [String] a string containing a key & value pair (e.g. "{\"Modality\":\"MR\"}")
|
180
|
+
#
|
181
|
+
def to_json
|
182
|
+
to_hash.to_json
|
183
|
+
end
|
184
|
+
|
185
|
+
# Gives a yaml string containing a human-readable representation of the Element.
|
186
|
+
#
|
187
|
+
# @return [String] a string containing a key & value pair (e.g. "---\nModality: MR\n")
|
188
|
+
#
|
189
|
+
def to_yaml
|
190
|
+
to_hash.to_yaml
|
191
|
+
end
|
192
|
+
|
193
|
+
# Gives the (decoded) value of the data element.
|
194
|
+
#
|
195
|
+
# @note Returned string values are automatically converted from their originally
|
196
|
+
# encoding (e.g. ISO8859-1 or ASCII-8BIT) to UTF-8 for convenience reasons.
|
197
|
+
# If the value string is wanted in its original encoding, extract the data
|
198
|
+
# element's bin attribute instead.
|
199
|
+
#
|
200
|
+
# @note Note that according to the DICOM Standard PS 3.5 C.12.1.1.2, the Character Set only applies
|
201
|
+
# to values of data elements of type SH, LO, ST, PN, LT or UT. Currently in ruby-dicom, all
|
202
|
+
# string values are encoding converted regardless of VR, but whether this causes any problems is uknown.
|
203
|
+
#
|
204
|
+
# @return [String, Integer, Float] the formatted element value
|
205
|
+
#
|
206
|
+
def value
|
207
|
+
if @value.is_a?(String)
|
208
|
+
# Unless this is actually the Character Set data element,
|
209
|
+
# get the character set (note that it may not be available):
|
210
|
+
character_set = (@tag != '0008,0005' && top_parent.is_a?(DObject)) ? top_parent.value('0008,0005') : nil
|
211
|
+
# Convert to UTF-8 from [original encoding]:
|
212
|
+
# In most cases the original encoding is IS0-8859-1 (ISO_IR 100), but if
|
213
|
+
# it is not specified in the DICOM object, or if the specified string
|
214
|
+
# is not recognized, ASCII-8BIT is assumed.
|
215
|
+
@value.encode('UTF-8', ENCODING_NAME[character_set])
|
216
|
+
# If unpleasant encoding exceptions occur, the below version may be considered:
|
217
|
+
#@value.encode('UTF-8', ENCODING_NAME[character_set], :invalid => :replace, :undef => :replace)
|
218
|
+
else
|
219
|
+
@value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Sets the value of the Element instance.
|
224
|
+
#
|
225
|
+
# In addition to updating the value attribute, the specified value is encoded to binary
|
226
|
+
# and used to update the Element's bin and length attributes too.
|
227
|
+
#
|
228
|
+
# @note The specified value must be of a type that is compatible with the Element's value representation (vr).
|
229
|
+
# @param [String, Integer, Float, Array] new_value a formatted value that is assigned to the element
|
230
|
+
#
|
231
|
+
def value=(new_value)
|
232
|
+
if VALUE_CONVERSION[@vr] == :to_s
|
233
|
+
# Unless this is actually the Character Set data element,
|
234
|
+
# get the character set (note that it may not be available):
|
235
|
+
character_set = (@tag != '0008,0005' && top_parent.is_a?(DObject)) ? top_parent.value('0008,0005') : nil
|
236
|
+
# Convert to [DObject encoding] from [input string encoding]:
|
237
|
+
# In most cases the DObject encoding is IS0-8859-1 (ISO_IR 100), but if
|
238
|
+
# it is not specified in the DICOM object, or if the specified string
|
239
|
+
# is not recognized, ASCII-8BIT is assumed.
|
240
|
+
@value = new_value.to_s.encode(ENCODING_NAME[character_set], new_value.to_s.encoding.name)
|
241
|
+
@bin = encode(@value)
|
242
|
+
else
|
243
|
+
# We may have an array (of numbers) which needs to be passed directly to
|
244
|
+
# the encode method instead of being forced into a numerical:
|
245
|
+
if new_value.is_a?(Array)
|
246
|
+
@value = new_value
|
247
|
+
@bin = encode(@value)
|
248
|
+
else
|
249
|
+
@value = new_value.send(VALUE_CONVERSION[@vr])
|
250
|
+
@bin = encode(@value)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
@length = @bin.length
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
|
260
|
+
# Encodes a formatted value to a binary string.
|
261
|
+
#
|
262
|
+
# @param [String, Integer, Float, Array] formatted_value a formatted value
|
263
|
+
# @return [String] the encoded, binary string
|
264
|
+
#
|
265
|
+
def encode(formatted_value)
|
266
|
+
stream.encode_value(formatted_value, @vr)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Collects the attributes of this instance.
|
270
|
+
#
|
271
|
+
# @return [Array<String>] an array of attributes
|
272
|
+
#
|
273
|
+
def state
|
274
|
+
[@tag, @vr, @value, @bin]
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
end
|