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
@@ -28,8 +28,6 @@ module DICOM
28
28
  # Creates a new AuditTrail instance.
29
29
  #
30
30
  def initialize
31
- # The AuditTrail requires JSON for serialization:
32
- require 'json'
33
31
  # Define the key/value hash used for tag records:
34
32
  @dictionary = Hash.new
35
33
  end
@@ -624,7 +624,7 @@ module DICOM
624
624
  @link.build_data_fragment(@data_elements, presentation_context_id)
625
625
  @link.transmit
626
626
  # Receive confirmation response:
627
- segments = @link.receive_single_transmission
627
+ segments = @link.receive_multiple_transmissions
628
628
  process_returned_data(segments)
629
629
  end
630
630
  # Close the DICOM link:
@@ -24,9 +24,9 @@ module DICOM
24
24
  @methods_from_names = Hash.new
25
25
  @names_from_methods = Hash.new
26
26
  # Load the elements dictionary:
27
- add_element_dictionary("#{ROOT_DIR}/dictionary/elements.txt")
27
+ add_element_dictionary("#{ROOT_DIR}/dictionary/elements.tsv")
28
28
  # Load the unique identifiers dictionary:
29
- add_uid_dictionary("#{ROOT_DIR}/dictionary/uids.txt")
29
+ add_uid_dictionary("#{ROOT_DIR}/dictionary/uids.tsv")
30
30
  end
31
31
 
32
32
  # Adds a custom DictionaryElement to the ruby-dicom element dictionary.
@@ -153,22 +153,11 @@ module DICOM
153
153
  if tag.private?
154
154
  element = DictionaryElement.new(tag, 'Private', ['UN'], '1', '')
155
155
  else
156
- if !(de = @elements["#{tag[0..3]},xxx#{tag[8]}"]).nil? # 1000,xxxh
157
- element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
158
- elsif !(de = @elements["#{tag[0..3]},xxxx"]).nil? # 1010,xxxx
159
- element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
160
- elsif !(de = @elements["#{tag[0..1]}xx,#{tag[5..8]}"]).nil? # hhxx,hhhh
161
- element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
162
- elsif !(de = @elements["#{tag[0..6]}x#{tag[8]}"]).nil? # 0028,hhxh
163
- element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
164
- else
165
- # We are facing an unknown (but not private) tag:
166
- element = DictionaryElement.new(tag, 'Unknown', ['UN'], '1', '')
167
- end
156
+ element = unknown_or_range_element(tag)
168
157
  end
169
158
  end
170
159
  end
171
- return element
160
+ element
172
161
  end
173
162
 
174
163
  # Extracts, and returns, all transfer syntaxes and SOP Classes from the dictionary.
@@ -230,6 +219,47 @@ module DICOM
230
219
  @uids[value]
231
220
  end
232
221
 
222
+
223
+ private
224
+
225
+
226
+ # Creates a list of possible 'range' tag candidates based on the given tag.
227
+ # Usually tags are uniquely defined in the DICOM dictionary, and the given
228
+ # tag can be matched directly. However, for a small set of known tags, the
229
+ # dictionary allows a range of tags to be associated with a specific
230
+ # entry. This method creates an array of candidate tags which are processed
231
+ # in order to match against these ranges.
232
+ #
233
+ # @param [String] tag the element tag
234
+ # @return [Array<String>] processed candidate tags
235
+ #
236
+ def range_candidates(tag)
237
+ [
238
+ "#{tag[0..3]},xxx#{tag[8]}", # 1000,xxxh
239
+ "#{tag[0..3]},xxxx", # 1010,xxxx
240
+ "#{tag[0..1]}xx,#{tag[5..8]}", # hhxx,hhhh
241
+ "#{tag[0..6]}x#{tag[8]}" # 0028,hhxh
242
+ ]
243
+ end
244
+
245
+ # Matches a tag against the possible range tag candidates, and if no match
246
+ # is found, returns a dictionary element representing an unknown tag.
247
+ #
248
+ # @param [String] tag the element tag
249
+ # @return [DictionaryElement] a matched range element or an unknown element
250
+ #
251
+ def unknown_or_range_element(tag)
252
+ element = nil
253
+ range_candidates(tag).each do |range_candidate_tag|
254
+ if de = @elements[range_candidate_tag]
255
+ element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
256
+ break
257
+ end
258
+ end
259
+ # If nothing was matched, we are facing an unknown (but not private) tag:
260
+ element ||= DictionaryElement.new(tag, 'Unknown', ['UN'], '1', '')
261
+ end
262
+
233
263
  end
234
264
 
235
265
  end
@@ -1,4 +1,4 @@
1
- # Copyright 2008-2013 Christoffer Lervag
1
+ # Copyright 2008-2014 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
@@ -49,7 +49,7 @@ module DICOM
49
49
  # @note Designed for the HTTP protocol only.
50
50
  # @note Whether this method should be included or removed from ruby-dicom is up for debate.
51
51
  #
52
- # @param [String] link a hyperlink string which specifies remote location of the DICOM file to be loaded.
52
+ # @param [String] link a hyperlink string which specifies remote location of the DICOM file to be loaded
53
53
  # @return [DObject] the created DObject instance
54
54
  #
55
55
  def self.get(link)
@@ -410,25 +410,25 @@ module DICOM
410
410
  # to ensure that a valid DICOM object is encoded.
411
411
  #
412
412
  def insert_missing_meta
413
- # File Meta Information Version:
414
- Element.new("0002,0001", [0,1], :parent => self) unless exists?("0002,0001")
415
- # Media Storage SOP Class UID:
416
- Element.new("0002,0002", value("0008,0016"), :parent => self) unless exists?("0002,0002")
417
- # Media Storage SOP Instance UID:
418
- Element.new("0002,0003", value("0008,0018"), :parent => self) unless exists?("0002,0003")
419
- # Transfer Syntax UID:
420
- Element.new("0002,0010", transfer_syntax, :parent => self) unless exists?("0002,0010")
421
- if !exists?("0002,0012") and !exists?("0002,0013")
413
+ {
414
+ '0002,0001' => [0,1], # File Meta Information Version
415
+ '0002,0002' => value('0008,0016'), # Media Storage SOP Class UID
416
+ '0002,0003' => value('0008,0018'), # Media Storage SOP Instance UID
417
+ '0002,0010' => transfer_syntax, # Transfer Syntax UID
418
+ '0002,0016' => DICOM.source_app_title, # Source Application Entity Title
419
+ }.each_pair do |tag, value|
420
+ add_element(tag, value) unless exists?(tag)
421
+ end
422
+ if !exists?("0002,0012") && !exists?("0002,0013")
422
423
  # Implementation Class UID:
423
- Element.new("0002,0012", UID_ROOT, :parent => self)
424
+ add_element("0002,0012", UID_ROOT)
424
425
  # Implementation Version Name:
425
- Element.new("0002,0013", NAME, :parent => self)
426
+ add_element("0002,0013", NAME)
426
427
  end
427
- # Source Application Entity Title:
428
- Element.new("0002,0016", DICOM.source_app_title, :parent => self) unless exists?("0002,0016")
429
- # Group Length: Delete the old one (if it exists) before creating a new one.
428
+ # Delete the old group length first (if it exists) to avoid a miscount
429
+ # in the coming group length determination.
430
430
  delete("0002,0000")
431
- Element.new("0002,0000", meta_group_length, :parent => self)
431
+ add_element("0002,0000", meta_group_length)
432
432
  end
433
433
 
434
434
  # Determines the length of the meta group in the DObject instance.
@@ -449,7 +449,7 @@ module DICOM
449
449
  end
450
450
  group_length += tag + vr + length + element.bin.length
451
451
  end
452
- return group_length
452
+ group_length
453
453
  end
454
454
 
455
455
  # Collects the attributes of this instance.
@@ -2,9 +2,35 @@ module DICOM
2
2
 
3
3
  class Parent
4
4
 
5
+ # Loads data from an encoded DICOM string and creates
6
+ # items and elements which are linked to this instance.
7
+ #
8
+ # @param [String] bin an encoded binary string containing DICOM information
9
+ # @param [String] syntax the transfer syntax to use when decoding the DICOM string
10
+ #
11
+ def parse(bin, syntax)
12
+ raise ArgumentError, "Invalid argument 'bin'. Expected String, got #{bin.class}." unless bin.is_a?(String)
13
+ raise ArgumentError, "Invalid argument 'syntax'. Expected String, got #{syntax.class}." unless syntax.is_a?(String)
14
+ read(bin, signature=false, :syntax => syntax)
15
+ end
16
+
17
+
5
18
  private
6
19
 
7
20
 
21
+ # Checks whether the given tag is a duplicate of an existing tag with this parent.
22
+ #
23
+ # @param [String] tag the tag of the candidate duplicate elemental
24
+ # @param [String] elemental the duplicate elemental type (e.g. Sequence, Element)
25
+ #
26
+ def check_duplicate(tag, elemental)
27
+ if @current_parent[tag]
28
+ gp = @current_parent.parent ? "#{@current_parent.parent.representation} => " : ''
29
+ p = @current_parent.representation
30
+ logger.warn("Duplicate #{elemental} (#{tag}) detected at level: #{gp}#{p}")
31
+ end
32
+ end
33
+
8
34
  # Checks for the official DICOM header signature.
9
35
  #
10
36
  # @return [Boolean] true if the proper signature is present, false if not, and nil if the string was shorter then the length of the DICOM signature
@@ -88,8 +114,7 @@ module DICOM
88
114
  # Create an Element from the gathered data:
89
115
  if level_vr == "SQ" or tag == ITEM_TAG
90
116
  if level_vr == "SQ"
91
- # Check for duplicate and create sequence:
92
- logger.warn("Duplicate Sequence (#{tag}) detected at level #{@current_parent.parent.is_a?(DObject) ? 'DObject' : @current_parent.parent.tag + ' => ' if @current_parent.parent}#{@current_parent.is_a?(DObject) ? 'DObject' : @current_parent.tag}") if @current_parent[tag]
117
+ check_duplicate(tag, 'Sequence')
93
118
  unless @current_parent[tag] and !@overwrite
94
119
  @current_element = Sequence.new(tag, :length => length, :name => name, :parent => @current_parent, :vr => vr)
95
120
  else
@@ -127,8 +152,7 @@ module DICOM
127
152
  # The occurance of such a tag indicates that a sequence or item has ended, and the parent must be changed:
128
153
  @current_parent = @current_parent.parent
129
154
  else
130
- # Check for duplicate and create an ordinary data element:
131
- logger.warn("Duplicate Element (#{tag}) detected at level #{@current_parent.parent.is_a?(DObject) ? 'DObject' : @current_parent.parent.tag + ' => ' if @current_parent.parent}#{@current_parent.is_a?(DObject) ? 'DObject' : @current_parent.tag}") if @current_parent[tag]
155
+ check_duplicate(tag, 'Element')
132
156
  unless @current_parent[tag] and !@overwrite
133
157
  @current_element = Element.new(tag, value, :bin => bin, :name => name, :parent => @current_parent, :vr => vr)
134
158
  # Check that the data stream didn't end abruptly:
@@ -17,36 +17,31 @@ module DICOM
17
17
  unless @segments
18
18
  @stream.add_last(string)
19
19
  else
20
- # As the encoded DICOM string will be cut in multiple, smaller pieces, we need to monitor the length of our encoded strings:
21
- if (string.length + @stream.length) > @max_size
22
- # Duplicate the string as not to ruin the binary of the data element with our slicing:
23
- segment = string.dup
24
- append = segment.slice!(0, @max_size-@stream.length)
25
- # Join these strings together and add them to the segments:
26
- @segments << @stream.export + append
27
- if (30 + segment.length) > @max_size
28
- # The remaining part of the string is bigger than the max limit, fill up more segments:
29
- # How many full segments will this string fill?
30
- number = (segment.length/@max_size.to_f).floor
31
- number.times {@segments << segment.slice!(0, @max_size)}
32
- # The remaining part is added to the stream:
33
- @stream.add_last(segment)
34
- else
35
- # The rest of the string is small enough that it can be added to the stream:
36
- @stream.add_last(segment)
37
- end
38
- elsif (30 + @stream.length) > @max_size
39
- # End the current segment, and start on a new segment for this string.
40
- @segments << @stream.export
41
- @stream.add_last(string)
42
- else
43
- # We are nowhere near the limit, simply add the string:
44
- @stream.add_last(string)
45
- end
20
+ add_with_segmentation(string)
46
21
  end
47
22
  end
48
23
  end
49
24
 
25
+ # Adds an encoded string to the output stream, while keeping track of the
26
+ # accumulated size of the output stream, splitting it up as necessary, and
27
+ # transferring the encoded string fragments to an array.
28
+ #
29
+ # @param [String] string a pre-encoded string
30
+ #
31
+ def add_with_segmentation(string)
32
+ # As the encoded DICOM string will be cut in multiple, smaller pieces, we need to monitor the length of our encoded strings:
33
+ if (string.length + @stream.length) > @max_size
34
+ split_and_add(string)
35
+ elsif (30 + @stream.length) > @max_size
36
+ # End the current segment, and start on a new segment for this string.
37
+ @segments << @stream.export
38
+ @stream.add_last(string)
39
+ else
40
+ # We are nowhere near the limit, simply add the string:
41
+ @stream.add_last(string)
42
+ end
43
+ end
44
+
50
45
  # Toggles the status for enclosed pixel data.
51
46
  #
52
47
  # @param [Element, Item, Sequence] element a data element
@@ -124,6 +119,34 @@ module DICOM
124
119
  end
125
120
  end
126
121
 
122
+ # Splits a pre-encoded string in parts and adds it to the segments instance
123
+ # array.
124
+ #
125
+ # @param [String] string a pre-encoded string
126
+ #
127
+ def split_and_add(string)
128
+ # Duplicate the string as not to ruin the binary of the data element with our slicing:
129
+ segment = string.dup
130
+ append = segment.slice!(0, @max_size-@stream.length)
131
+ # Clear out the stream along with a small part of the string:
132
+ @segments << @stream.export + append
133
+ if (30 + segment.length) > @max_size
134
+ # The remaining part of the string is bigger than the max limit, fill up more segments:
135
+ # How many full segments will this string fill?
136
+ number = (segment.length/@max_size.to_f).floor
137
+ start_index = 0
138
+ number.times {
139
+ @segments << segment.slice(start_index, @max_size)
140
+ start_index += @max_size
141
+ }
142
+ # The remaining part is added to the stream:
143
+ @stream.add_last(segment.slice(start_index, segment.length - start_index))
144
+ else
145
+ # The rest of the string is small enough that it can be added to the stream:
146
+ @stream.add_last(segment)
147
+ end
148
+ end
149
+
127
150
  # Encodes and writes a single data element.
128
151
  #
129
152
  # @param [Element, Item, Sequence] element a data element
@@ -35,7 +35,7 @@ module DICOM
35
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
36
  # Set instance variables:
37
37
  @tag = tag.upcase
38
- # We may beed to retrieve name and vr from the library:
38
+ # We may need to retrieve name and vr from the library:
39
39
  if options[:name] and options[:vr]
40
40
  @name = options[:name]
41
41
  @vr = options[:vr].upcase
@@ -212,9 +212,9 @@ module DICOM
212
212
  # In most cases the original encoding is IS0-8859-1 (ISO_IR 100), but if
213
213
  # it is not specified in the DICOM object, or if the specified string
214
214
  # is not recognized, ASCII-8BIT is assumed.
215
- @value.encode('UTF-8', ENCODING_NAME[character_set] || 'ASCII-8BIT')
215
+ @value.encode('UTF-8', ENCODING_NAME[character_set])
216
216
  # If unpleasant encoding exceptions occur, the below version may be considered:
217
- #@value.encode('UTF-8', ENCODING_NAME[character_set] || 'ASCII-8BIT', :invalid => :replace, :undef => :replace)
217
+ #@value.encode('UTF-8', ENCODING_NAME[character_set], :invalid => :replace, :undef => :replace)
218
218
  else
219
219
  @value
220
220
  end
@@ -229,8 +229,7 @@ module DICOM
229
229
  # @param [String, Integer, Float, Array] new_value a formatted value that is assigned to the element
230
230
  #
231
231
  def value=(new_value)
232
- conversion = VALUE_CONVERSION[@vr] || :to_s
233
- if conversion == :to_s
232
+ if VALUE_CONVERSION[@vr] == :to_s
234
233
  # Unless this is actually the Character Set data element,
235
234
  # get the character set (note that it may not be available):
236
235
  character_set = (@tag != '0008,0005' && top_parent.is_a?(DObject)) ? top_parent.value('0008,0005') : nil
@@ -238,7 +237,7 @@ module DICOM
238
237
  # In most cases the DObject encoding is IS0-8859-1 (ISO_IR 100), but if
239
238
  # it is not specified in the DICOM object, or if the specified string
240
239
  # is not recognized, ASCII-8BIT is assumed.
241
- @value = new_value.to_s.encode(ENCODING_NAME[character_set] || 'ASCII-8BIT', new_value.to_s.encoding.name)
240
+ @value = new_value.to_s.encode(ENCODING_NAME[character_set], new_value.to_s.encoding.name)
242
241
  @bin = encode(@value)
243
242
  else
244
243
  # We may have an array (of numbers) which needs to be passed directly to
@@ -247,7 +246,7 @@ module DICOM
247
246
  @value = new_value
248
247
  @bin = encode(@value)
249
248
  else
250
- @value = new_value.send(conversion)
249
+ @value = new_value.send(VALUE_CONVERSION[@vr])
251
250
  @bin = encode(@value)
252
251
  end
253
252
  end
@@ -117,4 +117,5 @@ module DICOM
117
117
  end
118
118
 
119
119
  end
120
+
120
121
  end
@@ -0,0 +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
+
64
+ end
@@ -0,0 +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
+
57
+ end