dicom 0.9.5 → 0.9.6

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