dicom 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,14 @@
1
1
  module DICOM
2
+
3
+ # This module is the general interface between the ImageItem class and the
4
+ # image methods found in the specific image processor modules.
5
+ #
2
6
  module ImageProcessor
3
7
 
4
8
  # Creates image objects from one or more compressed, binary string blobs.
5
- # Returns an array of images. If decompression fails, returns false.
6
- #
7
- # === Parameters
8
9
  #
9
- # * <tt>blobs</tt> -- Binary string blob(s) containing compressed pixel data.
10
+ # @param [Array<String>, String] blobs binary string blob(s) containing compressed pixel data
11
+ # @return [Array<MagickImage>, FalseClass] - an array of images, or false (if decompression failed)
10
12
  #
11
13
  def decompress(blobs)
12
14
  raise ArgumentError, "Expected Array or String, got #{blobs.class}." unless [String, Array].include?(blobs.class)
@@ -20,9 +22,9 @@ module DICOM
20
22
 
21
23
  # Extracts an array of pixels (integers) from an image object.
22
24
  #
23
- # === Parameters
24
- #
25
- # * <tt>image</tt> -- An Rmagick image object.
25
+ # @param [MagickImage] image a Magick image object
26
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
27
+ # @return [Array<Integer>] an array of pixel values
26
28
  #
27
29
  def export_pixels(image, photometry)
28
30
  raise ArgumentError, "Expected String, got #{photometry.class}." unless photometry.is_a?(String)
@@ -31,20 +33,21 @@ module DICOM
31
33
 
32
34
  # Creates an image object from a binary string blob.
33
35
  #
34
- # === Parameters
35
- #
36
- # * <tt>blob</tt> -- Binary string blob containing raw pixel data.
37
- # * <tt>columns</tt> -- Number of columns.
38
- # * <tt>rows</tt> -- Number of rows.
39
- # * <tt>depth</tt> -- Bit depth of the encoded pixel data.
40
- # * <tt>photometry</tt> -- String describing the DICOM photometry of the pixel data. Example: 'MONOCHROME1', 'RGB'.
36
+ # @param [String] blob binary string blob containing pixel data
37
+ # @param [Integer] columns the number of columns
38
+ # @param [Integer] rows the number of rows
39
+ # @param [Integer] depth the bit depth of the encoded pixel data
40
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
41
+ # @return [MagickImage] a Magick image object
41
42
  #
42
43
  def import_pixels(blob, columns, rows, depth, photometry)
43
44
  raise ArgumentError, "Expected String, got #{blob.class}." unless blob.is_a?(String)
44
45
  image_module.import_pixels(blob, columns, rows, depth, photometry)
45
46
  end
46
47
 
47
- # Returns an array containing the image objects that are supported by the image processor.
48
+ # Gives an array containing the image objects that are supported by the image processor.
49
+ #
50
+ # @return [Array] the valid image classes
48
51
  #
49
52
  def valid_image_objects
50
53
  return [Magick::Image, MiniMagick::Image]
@@ -54,6 +57,12 @@ module DICOM
54
57
  private
55
58
 
56
59
 
60
+ # Gives the specific image processor module corresponding to the specified
61
+ # image_processor module option.
62
+ #
63
+ # @raise [RuntimeError] if an unknown image processor is specified
64
+ # @return [DcmMiniMagick, DcmRMagick] the image processor module to be used
65
+ #
57
66
  def image_module
58
67
  case DICOM.image_processor
59
68
  when :mini_magick
@@ -1,15 +1,16 @@
1
1
  module DICOM
2
2
  module ImageProcessor
3
+
4
+ # This module contains methods for interacting with pixel data using the mini_magick gem.
5
+ #
3
6
  module DcmMiniMagick
4
7
 
5
8
  class << self
6
9
 
7
10
  # Creates image objects from an array of compressed, binary string blobs.
8
- # Returns an array of images. If decompression fails, returns false.
9
- #
10
- # === Parameters
11
11
  #
12
- # * <tt>blobs</tt> -- An array of binary string blobs containing compressed pixel data.
12
+ # @param [Array<String>] blobs an array of binary string blobs containing compressed pixel data
13
+ # @return [Array<MiniMagick::Image>, FalseClass] - an array of images, or false (if decompression failed)
13
14
  #
14
15
  def decompress(blobs)
15
16
  images = Array.new
@@ -22,39 +23,36 @@ module DICOM
22
23
 
23
24
  # Extracts an array of pixels (integers) from an image object.
24
25
  #
25
- # === Notes
26
- #
27
- # * This feature is not available as of yet in the mini_magick image processor. If this feature is needed, please try another image processor (RMagick).
26
+ # @note This feature is not available as of yet in the mini_magick image processor.
27
+ # If this feature is needed, please try another image processor (RMagick).
28
28
  #
29
- # === Parameters
30
- #
31
- # * <tt>image</tt> -- An MiniMagick image object.
29
+ # @param [MiniMagick::Image] image a mini_magick image object
30
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
31
+ # @return [Array<Integer>] an array of pixel values
32
32
  #
33
33
  def export_pixels(image, photometry)
34
34
  raise ArgumentError, "Expected MiniMagick::Image, got #{image.class}." unless image.is_a?(MiniMagick::Image)
35
35
  raise "Exporting pixels is not yet available with the mini_magick processor. Please try another image processor (RMagick)."
36
36
  end
37
37
 
38
- # Creates an image object from a binary string blob which contains raw pixel data.
39
- #
40
- # === Parameters
38
+ # Creates an image object from a binary string blob.
41
39
  #
42
- # * <tt>blob</tt> -- Binary string blob containing raw pixel data.
43
- # * <tt>columns</tt> -- Number of columns.
44
- # * <tt>rows</tt> -- Number of rows.
45
- # * <tt>depth</tt> -- Bit depth of the encoded pixel data.
46
- # * <tt>photometry</tt> -- String describing the DICOM photometry of the pixel data.
47
- # * <tt>format</tt> -- String describing the image format to be used when creating the image object. Defaults to 'png'.
40
+ # @param [String] blob binary string blob containing pixel data
41
+ # @param [Integer] columns the number of columns
42
+ # @param [Integer] rows the number of rows
43
+ # @param [Integer] depth the bit depth of the encoded pixel data
44
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
45
+ # @param [String] format the image format to use
46
+ # @return [Magick::Image] a mini_magick image object
48
47
  #
49
48
  def import_pixels(blob, columns, rows, depth, photometry, format="png")
50
49
  image = MiniMagick::Image.import_pixels(blob, columns, rows, depth, im_map(photometry), format)
51
50
  end
52
51
 
53
- # Returns an ImageMagick pixel map string based on the input DICOM photometry string.
54
- #
55
- # === Parameters
52
+ # Converts a given DICOM photometry string to a mini_magick pixel map string.
56
53
  #
57
- # * <tt>photometry</tt> -- String describing the photometry of the pixel data. Example: 'MONOCHROME1' or 'COLOR'.
54
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
55
+ # @return [String] a mini_magick pixel map string
58
56
  #
59
57
  def im_map(photometry)
60
58
  raise ArgumentError, "Expected String, got #{photometry.class}." unless photometry.is_a?(String)
@@ -1,35 +1,38 @@
1
1
  module DICOM
2
2
  module ImageProcessor
3
+
4
+ # This module contains methods for interacting with pixel data using the RMagick gem.
5
+ #
3
6
  module DcmRMagick
4
7
 
5
8
  class << self
6
9
 
7
10
  # Creates image objects from an array of compressed, binary string blobs.
8
- # Returns an array of images. If decompression fails, returns false.
9
11
  #
10
- # === Notes
12
+ # === Note
11
13
  #
12
- # The method tries to use RMagick of unpacking, but it seems that ImageMagick is not able to handle most of the
13
- # compressed image variants used in the DICOM standard. To get a more robust implementation which is able to handle
14
- # most types of compressed DICOM files, something else is needed.
14
+ # The method tries to use RMagick for unpacking, but unortunately, it seems that
15
+ # ImageMagick is not able to handle most of the compressed image variants used in the DICOM
16
+ # standard. To get a more robust implementation which is able to handle most types of
17
+ # compressed DICOM files, something else is needed.
15
18
  #
16
- # Probably a good candidate to use is the PVRG-JPEG library, which seems to be able to handle everything that is jpeg.
17
- # It exists in the Ubuntu repositories, where it can be installed and run through terminal. For source code, and some
18
- # additional information, check this link: http://www.panix.com/~eli/jpeg/
19
+ # Probably a good candidate to use is the PVRG-JPEG library, which seems to be able to handle
20
+ # everything that is jpeg. It exists in the Ubuntu repositories, where it can be installed and
21
+ # run through terminal. For source code, and some additional information, check out this link:
22
+ # http://www.panix.com/~eli/jpeg/
19
23
  #
20
24
  # Another idea would be to study how other open source libraries, like GDCM handle these files.
21
25
  #
22
- # === Parameters
23
- #
24
- # * <tt>blobs</tt> -- An array of binary string blobs containing compressed pixel data.
25
- #
26
- #--
27
- # The following transfer syntaxes have been verified as failing with ImageMagick:
28
- # TXS_JPEG_LOSSLESS_NH is not supported by (my) ImageMagick version: "Unsupported JPEG process: SOF type 0xc3"
29
- # TXS_JPEG_LOSSLESS_NH_FOP is not supported by (my) ImageMagick version: "Unsupported JPEG process: SOF type 0xc3"
30
- # TXS_JPEG_2000_PART1_LOSSLESS is not supported by (my) ImageMagick version: "jpc_dec_decodepkts failed"
26
+ # @param [Array<String>] blobs an array of binary string blobs containing compressed pixel data
27
+ # @return [Array<Magick::Image>, FalseClass] - an array of images, or false (if decompression failed)
31
28
  #
32
29
  def decompress(blobs)
30
+ # FIXME:
31
+ # The following transfer syntaxes have been verified as failing with ImageMagick:
32
+ # TXS_JPEG_LOSSLESS_NH is not supported by (my) ImageMagick version: "Unsupported JPEG process: SOF type 0xc3"
33
+ # TXS_JPEG_LOSSLESS_NH_FOP is not supported by (my) ImageMagick version: "Unsupported JPEG process: SOF type 0xc3"
34
+ # TXS_JPEG_2000_PART1_LOSSLESS is not supported by (my) ImageMagick version: "jpc_dec_decodepkts failed"
35
+ #
33
36
  images = Array.new
34
37
  # We attempt to decompress the pixels using ImageMagick:
35
38
  blobs.each do |string|
@@ -40,9 +43,9 @@ module DICOM
40
43
 
41
44
  # Extracts an array of pixels (integers) from an image object.
42
45
  #
43
- # === Parameters
44
- #
45
- # * <tt>image</tt> -- An Rmagick image object.
46
+ # @param [Magick::Image] image an RMagick image object
47
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
48
+ # @return [Array<Integer>] an array of pixel values
46
49
  #
47
50
  def export_pixels(image, photometry)
48
51
  raise ArgumentError, "Expected Magick::Image, got #{image.class}." unless image.is_a?(Magick::Image)
@@ -50,22 +53,25 @@ module DICOM
50
53
  return pixels
51
54
  end
52
55
 
53
- # Creates an image object from a binary string blob which contains raw pixel data.
56
+ # Creates an image object from a binary string blob.
54
57
  #
55
- # === Parameters
56
- #
57
- # * <tt>blob</tt> -- Binary string blob containing raw pixel data.
58
- # * <tt>columns</tt> -- Number of columns.
59
- # * <tt>rows</tt> -- Number of rows.
60
- # * <tt>depth</tt> -- Bit depth of the encoded pixel data.
61
- # * <tt>photometry</tt> -- String describing the DICOM photometry of the pixel data.
62
- # * <tt>format</tt> -- String describing the image format to be used when creating the image object. Defaults to 'png'.
58
+ # @param [String] blob binary string blob containing pixel data
59
+ # @param [Integer] columns the number of columns
60
+ # @param [Integer] rows the number of rows
61
+ # @param [Integer] depth the bit depth of the encoded pixel data
62
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
63
+ # @param [String] format the image format to use
64
+ # @return [Magick::Image] an RMagick image object
63
65
  #
64
66
  def import_pixels(blob, columns, rows, depth, photometry, format="png")
65
67
  image = Magick::Image.new(columns,rows).import_pixels(0, 0, columns, rows, rm_map(photometry), blob, rm_data_type(depth))
66
68
  end
67
69
 
68
- # Returns the RMagick StorageType pixel value corresponding to the given bit length.
70
+ # Converts a given bit depth to an RMagick StorageType.
71
+ #
72
+ # @raise [ArgumentError] if given an unsupported bit depth
73
+ # @param [Integer] bit_depth the bit depth of the pixel data
74
+ # @return [Magick::CharPixel, Magick::ShortPixel] the proper storage type
69
75
  #
70
76
  def rm_data_type(bit_depth)
71
77
  return case bit_depth
@@ -78,11 +84,10 @@ module DICOM
78
84
  end
79
85
  end
80
86
 
81
- # Returns an RMagick pixel map string based on the input DICOM photometry string.
82
- #
83
- # === Parameters
87
+ # Converts a given DICOM photometry string to an RMagick pixel map string.
84
88
  #
85
- # * <tt>photometry</tt> -- String describing the photometry of the pixel data. Example: 'MONOCHROME1' or 'COLOR'.
89
+ # @param [String] photometry a code describing the photometry of the pixel data (e.g. 'MONOCHROME1' or 'COLOR')
90
+ # @return [String] an RMagick pixel map string
86
91
  #
87
92
  def rm_map(photometry)
88
93
  raise ArgumentError, "Expected String, got #{photometry.class}." unless photometry.is_a?(String)
@@ -1,121 +1,134 @@
1
- module DICOM
2
-
3
- # The Item class handles information related to items - the elements contained in sequences.
4
- #
5
- # === Inheritance
6
- #
7
- # As the Item class inherits from the ImageItem class, which itself inherits from the Parent class,
8
- # all ImageItem and Parent methods are also available to instances of Item.
9
- #
10
- class Item < ImageItem
11
-
12
- # Include the Elemental mix-in module:
13
- include Elemental
14
-
15
- # The index of this Item in the group of items belonging to its parent. If the Item is without parent, index is nil.
16
- attr_accessor :index
17
-
18
- # Creates an Item instance.
19
- #
20
- # === Notes
21
- #
22
- # Normally, an Item contains data elements and/or sequences. However, in some cases, an Item will instead/also
23
- # carry binary string data, like the pixel data of an encapsulated image fragment.
24
- #
25
- # === Parameters
26
- #
27
- # * <tt>options</tt> -- A hash of parameters.
28
- #
29
- # === Options
30
- #
31
- # * <tt>:bin</tt> -- A binary string to be carried by the Item.
32
- # * <tt>:index</tt> -- Fixnum. If the Item is to be inserted at a specific index (Item number), this option parameter needs to set.
33
- # * <tt>:length</tt> -- Fixnum. The Item length (which either refers to the length of the encoded string of children of this Item, or the length of its binary data).
34
- # * <tt>:name</tt> - String. The name of the Item may be specified upon creation. If it is not, a default name is chosen.
35
- # * <tt>:parent</tt> - Sequence or DObject instance which the Item instance shall belong to.
36
- # * <tt>:vr</tt> -- String. The value representation of the Item may be specified upon creation. If it is not, a default vr is chosen.
37
- #
38
- # === Examples
39
- #
40
- # # Create an empty Item and connect it to the "Structure Set ROI Sequence":
41
- # item = Item.new(:parent => dcm["3006,0020"])
42
- # # Create a "Pixel Data Item" which carries an encapsulated image frame (a pre-encoded binary):
43
- # pixel_item = Item.new(:bin => processed_pixel_data, :parent => dcm["7FE0,0010"][1])
44
- #
45
- def initialize(options={})
46
- # Set common parent variables:
47
- initialize_parent
48
- # Set instance variables:
49
- @tag = ITEM_TAG
50
- @value = nil
51
- @name = options[:name] || "Item"
52
- @vr = options[:vr] || ITEM_VR
53
- if options[:bin]
54
- self.bin = options[:bin]
55
- else
56
- @length = options[:length] || -1
57
- end
58
- if options[:parent]
59
- @parent = options[:parent]
60
- @index = options[:index] if options[:index]
61
- @parent.add_item(self, :index => options[:index], :no_follow => true)
62
- end
63
- end
64
-
65
- # Returns true if the argument is an instance with attributes equal to self.
66
- #
67
- def ==(other)
68
- if other.respond_to?(:to_item)
69
- other.send(:state) == state
70
- end
71
- end
72
-
73
- alias_method :eql?, :==
74
-
75
- # Sets the binary string that the Item will contain.
76
- #
77
- # === Parameters
78
- #
79
- # * <tt>new_bin</tt> -- A binary string of encoded data.
80
- #
81
- # === Examples
82
- #
83
- # # Insert a custom jpeg in the (encapsulated) pixel data element, in it's first pixel data item:
84
- # dcm["7FE0,0010"][1].children.first.bin = jpeg_binary_string
85
- #
86
- def bin=(new_bin)
87
- raise ArgumentError, "Invalid parameter type. String was expected, got #{new_bin.class}." unless new_bin.is_a?(String)
88
- # Add an empty byte at the end if the length of the binary is odd:
89
- if new_bin.length.odd?
90
- @bin = new_bin + "\x00"
91
- else
92
- @bin = new_bin
93
- end
94
- @value = nil
95
- @length = @bin.length
96
- end
97
-
98
- # Generates a Fixnum hash value for this instance.
99
- #
100
- def hash
101
- state.hash
102
- end
103
-
104
- # Returns self.
105
- #
106
- def to_item
107
- self
108
- end
109
-
110
-
111
- private
112
-
113
-
114
- # Returns the attributes of this instance in an array (for comparison purposes).
115
- #
116
- def state
117
- [@vr, @name, @tags]
118
- end
119
-
120
- end
1
+ module DICOM
2
+
3
+ # The Item class handles information related to items - the elements contained in sequences.
4
+ #
5
+ # === Inheritance
6
+ #
7
+ # As the Item class inherits from the ImageItem class, which itself inherits from the Parent class,
8
+ # all ImageItem and Parent methods are also available to instances of Item.
9
+ #
10
+ class Item < ImageItem
11
+
12
+ # Include the Elemental mix-in module:
13
+ include Elemental
14
+
15
+ # The index of this Item in the group of items belonging to its parent. If the Item is without parent, index is nil.
16
+ attr_accessor :index
17
+
18
+ # Creates an Item instance.
19
+ #
20
+ # Normally, an Item contains data elements and/or sequences. However,
21
+ # in some cases, an Item will instead/also carry binary string data,
22
+ # like the pixel data of an encapsulated image fragment.
23
+ #
24
+ # @param [Hash] options the options to use for creating the item
25
+ # @option options [String] :bin a binary string to be carried by the item
26
+ # @option options [String] :indexif the item is to be inserted at a specific index (Item number), this option parameter needs to set
27
+ # @option options [String] :length theiItem length (which either refers to the length of the encoded string of children of this item, or the length of its binary data)
28
+ # @option options [String] :name the name of the item may be specified upon creation (if not, a default name is used)
29
+ # @option options [String] :parent a Sequence or DObject instance which the item instance shall belong to
30
+ # @option options [String] :vr the value representation of the item may be specified upon creation (if not, a default vr is used)
31
+ #
32
+ # @example Create an empty Item and connect it to the "Structure Set ROI Sequence"
33
+ # item = Item.new(:parent => dcm["3006,0020"])
34
+ # @example Create a "Pixel Data Item" which carries an encapsulated image frame (a pre-encoded binary)
35
+ # pixel_item = Item.new(:bin => processed_pixel_data, :parent => dcm["7FE0,0010"][1])
36
+ #
37
+ def initialize(options={})
38
+ # Set common parent variables:
39
+ initialize_parent
40
+ # Set instance variables:
41
+ @tag = ITEM_TAG
42
+ @value = nil
43
+ @name = options[:name] || "Item"
44
+ @vr = options[:vr] || ITEM_VR
45
+ if options[:bin]
46
+ self.bin = options[:bin]
47
+ else
48
+ @length = options[:length] || -1
49
+ end
50
+ if options[:parent]
51
+ @parent = options[:parent]
52
+ @index = options[:index] if options[:index]
53
+ @parent.add_item(self, :index => options[:index], :no_follow => true)
54
+ end
55
+ end
56
+
57
+ # Checks for equality.
58
+ #
59
+ # Other and self are considered equivalent if they are
60
+ # of compatible types and their attributes are equivalent.
61
+ #
62
+ # @param other an object to be compared with self.
63
+ # @return [Boolean] true if self and other are considered equivalent
64
+ #
65
+ def ==(other)
66
+ if other.respond_to?(:to_item)
67
+ other.send(:state) == state
68
+ end
69
+ end
70
+
71
+ alias_method :eql?, :==
72
+
73
+ # Sets the binary string that the Item will contain.
74
+ #
75
+ # @param [String] new_bin a binary string of encoded data
76
+ # @example Insert a custom jpeg in the (encapsulated) pixel data element (in it's first pixel data item)
77
+ # dcm['7FE0,0010'][1].children.first.bin = jpeg_binary_string
78
+ #
79
+ def bin=(new_bin)
80
+ raise ArgumentError, "Invalid parameter type. String was expected, got #{new_bin.class}." unless new_bin.is_a?(String)
81
+ # Add an empty byte at the end if the length of the binary is odd:
82
+ if new_bin.length.odd?
83
+ @bin = new_bin + "\x00"
84
+ else
85
+ @bin = new_bin
86
+ end
87
+ @value = nil
88
+ @length = @bin.length
89
+ end
90
+
91
+ # Computes a hash code for this object.
92
+ #
93
+ # @note Two objects with the same attributes will have the same hash code.
94
+ #
95
+ # @return [Fixnum] the object's hash code
96
+ #
97
+ def hash
98
+ state.hash
99
+ end
100
+
101
+ # Loads data from an encoded DICOM string and creates
102
+ # sequences and elements which are linked to this instance.
103
+ #
104
+ # @param [String] bin an encoded binary string containing DICOM information
105
+ # @param [String] syntax the transfer syntax to use when decoding the DICOM string
106
+ #
107
+ def parse(bin, syntax)
108
+ raise ArgumentError, "Invalid argument 'bin'. Expected String, got #{bin.class}." unless bin.is_a?(String)
109
+ raise ArgumentError, "Invalid argument 'syntax'. Expected String, got #{syntax.class}." unless syntax.is_a?(String)
110
+ read(bin, signature=false, :syntax => syntax)
111
+ end
112
+
113
+ # Returns self.
114
+ #
115
+ # @return [Item] self
116
+ #
117
+ def to_item
118
+ self
119
+ end
120
+
121
+
122
+ private
123
+
124
+
125
+ # Collects the attributes of this instance.
126
+ #
127
+ # @return [Array<String, Sequence, Element>] an array of attributes
128
+ #
129
+ def state
130
+ [@vr, @name, @tags]
131
+ end
132
+
133
+ end
121
134
  end