dicom 0.9.5 → 0.9.6

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.
Files changed (39) hide show
  1. checksums.yaml +13 -5
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +50 -30
  3. data/{CONTRIBUTING.rdoc → CONTRIBUTING.md} +16 -16
  4. data/Gemfile.lock +47 -0
  5. data/README.md +152 -0
  6. data/dicom.gemspec +11 -10
  7. data/lib/dicom.rb +30 -11
  8. data/lib/dicom/anonymizer.rb +654 -649
  9. data/lib/dicom/audit_trail.rb +0 -2
  10. data/lib/dicom/d_client.rb +1 -1
  11. data/lib/dicom/d_library.rb +45 -15
  12. data/lib/dicom/d_object.rb +18 -18
  13. data/lib/dicom/d_read.rb +28 -4
  14. data/lib/dicom/d_write.rb +49 -26
  15. data/lib/dicom/dictionary/{elements.txt → elements.tsv} +0 -0
  16. data/lib/dicom/dictionary/{uids.txt → uids.tsv} +0 -0
  17. data/lib/dicom/element.rb +6 -7
  18. data/lib/dicom/elemental.rb +1 -0
  19. data/lib/dicom/elemental_parent.rb +64 -0
  20. data/lib/dicom/extensions/array.rb +57 -0
  21. data/lib/dicom/extensions/hash.rb +31 -0
  22. data/lib/dicom/extensions/string.rb +126 -0
  23. data/lib/dicom/{constants.rb → general/constants.rb} +29 -38
  24. data/lib/dicom/{deprecated.rb → general/deprecated.rb} +0 -0
  25. data/lib/dicom/{logging.rb → general/logging.rb} +0 -0
  26. data/lib/dicom/{variables.rb → general/methods.rb} +0 -22
  27. data/lib/dicom/general/variables.rb +29 -0
  28. data/lib/dicom/{version.rb → general/version.rb} +1 -1
  29. data/lib/dicom/image_item.rb +0 -2
  30. data/lib/dicom/image_processor.rb +2 -0
  31. data/lib/dicom/item.rb +1 -13
  32. data/lib/dicom/link.rb +2 -1
  33. data/lib/dicom/parent.rb +34 -86
  34. data/lib/dicom/sequence.rb +1 -13
  35. data/lib/dicom/stream.rb +94 -114
  36. data/rakefile.rb +1 -1
  37. metadata +73 -36
  38. data/README.rdoc +0 -149
  39. data/lib/dicom/ruby_extensions.rb +0 -249
@@ -1,149 +0,0 @@
1
- = RUBY DICOM
2
-
3
- Ruby DICOM is a small and simple library for handling DICOM in Ruby. DICOM (Digital Imaging
4
- and Communications in Medicine) is a standard for handling, storing, printing,
5
- and transmitting information in medical imaging. It includes a file format definition
6
- and a network communications protocol. Ruby DICOM supports reading from, editing
7
- and writing to this file format. It also features basic support for select network
8
- communication modalities like querying, moving, sending and receiving files.
9
-
10
-
11
- == INSTALLATION
12
-
13
- gem install dicom
14
-
15
-
16
- == REQUIREMENTS
17
-
18
- * Ruby 1.9.2 (if you are still on Ruby 1.8, gems up to version 0.9.1 can be used)
19
-
20
-
21
- == BASIC USAGE
22
-
23
- === Load & Include
24
-
25
- require 'dicom'
26
- include DICOM
27
-
28
- === Read, modify and write
29
-
30
- # Read file:
31
- dcm = DObject.read("some_file.dcm")
32
- # Extract the Patient's Name value:
33
- dcm.patients_name.value
34
- # Add or modify the Patient's Name element:
35
- dcm.patients_name = "Anonymous"
36
- # Remove a data element from the DICOM object:
37
- dcm.pixel_data = nil
38
- # Write to file:
39
- dcm.write("new_file.dcm")
40
-
41
- === Modify using tag strings instead of dictionary method names
42
-
43
- # Extract the Patient's Name value:
44
- dcm.value("0010,0010")
45
- # Modify the Patient's Name element:
46
- dcm["0010,0010"].value = "Anonymous"
47
- # Delete a data element from the DICOM object:
48
- dcm.delete("7FE0,0010")
49
-
50
- === Extracting information about the DICOM object
51
-
52
- # Display a short summary of the file's properties:
53
- dcm.summary
54
- # Print all data elements to screen:
55
- dcm.print
56
- # Convert the data element hierarchy to a nested hash:
57
- dcm.to_hash
58
-
59
- === Handle pixel data
60
-
61
- # Retrieve the pixel data in a Ruby Array:
62
- dcm.pixels
63
- # Load the pixel data to an numerical array (NArray):
64
- dcm.narray
65
- # Load the pixel data to an RMagick image object and display it on the screen:
66
- dcm.image.display
67
-
68
- === Transmit a DICOM file
69
-
70
- # Send a local file to a server (PACS) over the network:
71
- node = DClient.new("10.1.25.200", 104)
72
- node.send("some_file.dcm")
73
-
74
- === Start a DICOM server
75
-
76
- # Initiate a simple storage provider which can receive DICOM files of all modalities:
77
- s = DServer.new(104, :host_ae => "MY_DICOM_SERVER")
78
- s.start_scp("C:/temp/")
79
-
80
- === Log settings
81
-
82
- # Change the log level so that only error messages are displayed:
83
- DICOM.logger.level = Logger::ERROR
84
- # Setting up a simple file log:
85
- l = Logger.new('my_logfile.log')
86
- DICOM.logger = l
87
- # Create a logger which ages logfile daily/monthly:
88
- DICOM.logger = Logger.new('foo.log', 'daily')
89
- DICOM.logger = Logger.new('foo.log', 'monthly')
90
-
91
-
92
- === IRB Tip
93
-
94
- When working with Ruby DICOM in irb, you may be annoyed with all the information
95
- that is printed to screen, regardless if you have set verbose as false. This is because
96
- in irb every variable loaded in the program is automatically printed to the screen.
97
- A useful hack to avoid this effect is to append ";0" after a command.
98
- Example:
99
- dcm = DObject.read("some_file.dcm") ;0
100
-
101
-
102
- == RESOURCES
103
-
104
- * {Official home page}[http://dicom.rubyforge.org/]
105
- * {Discussion forum}[http://groups.google.com/group/ruby-dicom]
106
- * {Documentation}[http://rubydoc.info/gems/dicom/frames]
107
- * {Tutorials}[http://dicom.rubyforge.org/tutorials.html]
108
- * {Source code repository}[https://github.com/dicom/ruby-dicom]
109
-
110
-
111
- == COPYRIGHT
112
-
113
- Copyright 2008-2013 Christoffer Lervåg
114
-
115
- This program is free software: you can redistribute it and/or modify
116
- it under the terms of the GNU General Public License as published by
117
- the Free Software Foundation, either version 3 of the License, or
118
- (at your option) any later version.
119
-
120
- This program is distributed in the hope that it will be useful,
121
- but WITHOUT ANY WARRANTY; without even the implied warranty of
122
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
123
- GNU General Public License for more details.
124
-
125
- You should have received a copy of the GNU General Public License
126
- along with this program. If not, see http://www.gnu.org/licenses/ .
127
-
128
-
129
- == ABOUT THE AUTHOR
130
-
131
- * Name: Christoffer Lervåg
132
- * Location: Norway
133
- * Email: chris.lervag [@nospam.com] @gmail.com
134
-
135
- Please don't hesitate to email me if you have any feedback related to this project!
136
-
137
-
138
- == CONTRIBUTORS
139
-
140
- * {Christoffer Lervåg}[https://github.com/dicom]
141
- * {John Axel Eriksson}[https://github.com/johnae]
142
- * {Kamil Bujniewicz}[https://github.com/icdark]
143
- * {Jeff Miller}[https://github.com/jeffmax]
144
- * {Donnie Millar}[https://github.com/dmillar]
145
- * {Björn Albers}[https://github.com/bjoernalbers]
146
- * {Felix Petriconi}[https://github.com/FelixPetriconi]
147
- * {Steven Bedrick}[https://github.com/stevenbedrick]
148
- * {Lars Benner}[https://github.com/Maturin]
149
- * {Brett Goulder}[https://github.com/brettgoulder]
@@ -1,249 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # This file contains extensions to the Ruby library which are used by Ruby DICOM.
4
-
5
- # Extension to the String class. These mainly facilitate the processing and analysis of element tags.
6
- # A tag string (as used by the ruby-dicom library) is 9 characters long and of the form 'GGGG,EEEE'
7
- # (where G represents a group hexadecimal, and E represents an element hexadecimal).
8
- #
9
- class String
10
-
11
- # Renames the original unpack method.
12
- #
13
- alias __original_unpack__ unpack
14
-
15
- # Checks if a string value LOOKS like a DICOM name - it may still not be valid one.
16
- #
17
- # @return [Boolean] true if a string looks like a DICOM name, and false if not
18
- #
19
- def dicom_name?
20
- self == self.dicom_titleize
21
- end
22
-
23
- # Checks if a string value LOOKS like a DICOM method name - it may still not be valid one.
24
- #
25
- # @return [Boolean] true if a string looks like a DICOM method name, and false if not
26
- #
27
- def dicom_method?
28
- self == self.dicom_underscore
29
- end
30
-
31
- # Capitalizes all the words in the string and replaces some characters to make a nicer looking title.
32
- #
33
- # @return [String] a formatted, capitalized string
34
- #
35
- def dicom_titleize
36
- self.dicom_underscore.gsub(/_/, ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
37
- end
38
-
39
- # Makes an underscored, lowercased version of the string.
40
- #
41
- # @return [String] an underscored, lower case string
42
- #
43
- def dicom_underscore
44
- word = self.dup
45
- word.tr!('-', '_')
46
- word.downcase!
47
- word
48
- end
49
-
50
- # Divides a string into a number of sub-strings of exactly equal length.
51
- #
52
- # @note The length of self must be a multiple of parts, or an exception will be raised.
53
- # @param [Integer] parts the number of sub-strings to create
54
- # @return [Array<String>] the divided sub-strings
55
- #
56
- def divide(parts)
57
- raise ArgumentError, "Expected an integer (Fixnum). Got #{parts.class}." unless parts.is_a?(Fixnum)
58
- raise ArgumentError, "Argument must be in the range <1 - self.length (#{self.length})>. Got #{parts}." if parts < 1 or parts > self.length
59
- raise ArgumentError, "Length of self (#{self.length}) must be a multiple of parts (#{parts})." unless (self.length/parts).to_f == self.length/parts.to_f
60
- if parts > 1
61
- sub_strings = Array.new
62
- sub_length = self.length/parts
63
- parts.times { sub_strings << self.slice!(0..(sub_length-1)) }
64
- return sub_strings
65
- else
66
- return [self]
67
- end
68
- end
69
-
70
- # Extracts the element part of the tag string: The last 4 characters.
71
- #
72
- # @return [String] the element part of the tag
73
- #
74
- def element
75
- return self[5..8]
76
- end
77
-
78
- # Returns the group part of the tag string: The first 4 characters.
79
- #
80
- # @return [String] the group part of the tag
81
- #
82
- def group
83
- return self[0..3]
84
- end
85
-
86
- # Returns the "Group Length" ('GGGG,0000') tag which corresponds to the original tag/group string.
87
- # The original string may either be a 4 character group string, or a 9 character custom tag.
88
- #
89
- # @return [String] a group length tag
90
- #
91
- def group_length
92
- if self.length == 4
93
- return self + ',0000'
94
- else
95
- return self.group + ',0000'
96
- end
97
- end
98
-
99
- # Checks if the string is a "Group Length" tag (its element part is '0000').
100
- #
101
- # @return [Boolean] true if it is a group length tag, and false if not
102
- #
103
- def group_length?
104
- return (self.element == '0000' ? true : false)
105
- end
106
-
107
- # Checks if the string is a private tag (has an odd group number).
108
- #
109
- # @return [Boolean] true if it is a private tag, and false if not
110
- #
111
- def private?
112
- return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true)
113
- end
114
-
115
- # Checks if the string is a valid tag (as defined by ruby-dicom: 'GGGG,EEEE').
116
- #
117
- # @return [Boolean] true if it is a valid tag, and false if not
118
- #
119
- def tag?
120
- # Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
121
- return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true)
122
- end
123
-
124
- # Converts the string to a proper DICOM element method name symbol.
125
- #
126
- # @return [Symbol] a DICOM element method name
127
- #
128
- def to_element_method
129
- self.gsub(/^3/,'three_').gsub(/[#*?!]/,' ').gsub(', ',' ').gsub('&','and').gsub(' - ','_').gsub(' / ','_').gsub(/[\s\-\.\,\/\\]/,'_').gsub(/[\(\)\']/,'').gsub(/\_+/, '_').downcase.to_sym
130
- end
131
-
132
- # Redefines the core library unpack method, adding
133
- # the ability to decode signed integers in big endian.
134
- #
135
- # @param [String] format a format string which decides the decoding scheme to use
136
- # @return [Array<String, Integer, Float>] the decoded values
137
- #
138
- def unpack(format)
139
- # Check for some custom unpack strings that we've invented:
140
- case format
141
- when "k*" # SS
142
- # Unpack BE US, repack LE US, then finally unpack LE SS:
143
- wrongly_unpacked = self.__original_unpack__('n*')
144
- repacked = wrongly_unpacked.__original_pack__('S*')
145
- correct = repacked.__original_unpack__('s*')
146
- when 'r*' # SL
147
- # Unpack BE UL, repack LE UL, then finally unpack LE SL:
148
- wrongly_unpacked = self.__original_unpack__('N*')
149
- repacked = wrongly_unpacked.__original_pack__('I*')
150
- correct = repacked.__original_unpack__('l*')
151
- else
152
- # Call the original method for all other (normal) cases:
153
- self.__original_unpack__(format)
154
- end
155
- end
156
-
157
- end
158
-
159
-
160
- # Extensions to the Array class.
161
- # These mainly deal with encoding integer arrays as well as conversion between
162
- # signed and unsigned integers.
163
- #
164
- class Array
165
-
166
- # Renames the original pack method.
167
- #
168
- alias __original_pack__ pack
169
-
170
- # Redefines the old pack method, adding the ability to encode signed integers in big endian
171
- # (which surprisingly has not been supported out of the box in Ruby until version 1.9.3).
172
- #
173
- # @param [String] format a format string which decides the encoding scheme to use
174
- # @return [String] the encoded binary string
175
- #
176
- def pack(format)
177
- # FIXME: At some time in the future, when Ruby 1.9.3 can be set as required ruby version,
178
- # this custom pack (as well as unpack) method can be discarded, and the desired endian
179
- # encodings can probably be achieved with the new template strings introduced in 1.9.3.
180
- #
181
- # Check for some custom pack strings that we've invented:
182
- case format
183
- when 'k*' # SS
184
- # Pack LE SS, re-unpack as LE US, then finally pack BE US:
185
- wrongly_packed = self.__original_pack__('s*')
186
- reunpacked = wrongly_packed.__original_unpack__('S*')
187
- correct = reunpacked.__original_pack__('n*')
188
- when 'r*' # SL
189
- # Pack LE SL, re-unpack as LE UL, then finally pack BE UL:
190
- wrongly_packed = self.__original_pack__('l*')
191
- reunpacked = wrongly_packed.__original_unpack__('I*')
192
- correct = reunpacked.__original_pack__('N*')
193
- else
194
- # Call the original method for all other (normal) cases:
195
- self.__original_pack__(format)
196
- end
197
- end
198
-
199
- # Packs an array of (unsigned) integers to a binary string (blob).
200
- #
201
- # @param [Integer] depth the bit depth to be used when encoding the unsigned integers
202
- # @return [String] an encoded binary string
203
- #
204
- def to_blob(depth)
205
- raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
206
- raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
207
- case depth
208
- when 8
209
- return self.pack('C*') # Unsigned char
210
- when 16
211
- return self.pack('S*') # Unsigned short, native byte order
212
- end
213
- end
214
-
215
- # Shifts the integer values of the array to make a signed data set.
216
- # The size of the shift is determined by the given bit depth.
217
- #
218
- # @param [Integer] depth the bit depth of the integers
219
- # @return [Array<Integer>] an array of signed integers
220
- #
221
- def to_signed(depth)
222
- raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
223
- raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
224
- case depth
225
- when 8
226
- return self.collect {|i| i - 128}
227
- when 16
228
- return self.collect {|i| i - 32768}
229
- end
230
- end
231
-
232
- # Shifts the integer values of the array to make an unsigned data set.
233
- # The size of the shift is determined by the given bit depth.
234
- #
235
- # @param [Integer] depth the bit depth of the integers
236
- # @return [Array<Integer>] an array of unsigned integers
237
- #
238
- def to_unsigned(depth)
239
- raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
240
- raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
241
- case depth
242
- when 8
243
- return self.collect {|i| i + 128}
244
- when 16
245
- return self.collect {|i| i + 32768}
246
- end
247
- end
248
-
249
- end