dicom 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|