dicom 0.6 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/DOCUMENTATION +214 -214
- data/lib/DClient.rb +18 -14
- data/lib/DLibrary.rb +32 -32
- data/lib/DObject.rb +40 -39
- data/lib/DRead.rb +0 -0
- data/lib/DServer.rb +0 -0
- data/lib/DWrite.rb +15 -15
- data/lib/Link.rb +0 -0
- data/lib/Stream.rb +6 -3
- data/lib/dicom.rb +0 -0
- data/lib/ruby_extensions.rb +0 -0
- metadata +3 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
= 0.6.1
|
2
|
+
|
3
|
+
=== 23rd August, 2009
|
4
|
+
|
5
|
+
* Fixed a bug (introduced in version 0.6) where the creation of new data elements caused an error.
|
6
|
+
* Fixed a bug (introduced in version 0.6) where compressed pixel data were no longer detected correctly.
|
7
|
+
* Fixed a bug where writing a DICOM file with no path prefix failed.
|
8
|
+
* Fixed a bug where creating a new data element in a DICOM file with tag hierarchies gave an invalid DICOM file.
|
9
|
+
* Fixed a bug where retrieving a RMagick image from DICOM files containing two sets of image data failed.
|
10
|
+
|
11
|
+
|
1
12
|
= 0.6
|
2
13
|
|
3
14
|
=== 13th August, 2009
|
data/DOCUMENTATION
CHANGED
@@ -16,240 +16,240 @@ CLASS DLibrary
|
|
16
16
|
|
17
17
|
PUBLIC CLASS METHODS
|
18
18
|
|
19
|
-
|
19
|
+
new()
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
Initialize a new Library (dictionary) object.
|
22
|
+
Useful if you want to make a script that reads hundreds or thousands of DICOM files,
|
23
|
+
because you can save time by loading the library one time at startup instead of
|
24
|
+
having the library being loaded for each DICOM file being read.
|
25
|
+
Example:
|
26
|
+
myLib = DICOM::DLibrary.new
|
27
27
|
|
28
28
|
|
29
29
|
CLASS DObject
|
30
30
|
|
31
31
|
PUBLIC CLASS METHODS
|
32
32
|
|
33
|
-
|
33
|
+
new(filename, options={})
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
Initialize a new DICOM object.
|
36
|
+
Example 1: (The simplest way)
|
37
|
+
require 'dicom'
|
38
|
+
obj = DICOM::DObject.new("myFile.dcm")
|
39
|
+
Example 2: (Using a pre-loaded library to speed up reading when reading multiple files)
|
40
|
+
obj = DICOM::DObject.new("myFile.dcm", :verbose => false, :lib => myLib)
|
41
|
+
Example 3: (Open an empty DICOM object)
|
42
|
+
obj = DICOM::DObject.new(nil)
|
43
43
|
|
44
44
|
ACCESSORS (read only)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
45
|
+
:read_success
|
46
|
+
A boolean that is true if DICOM object was read successfully, and false if not.
|
47
|
+
:write_success
|
48
|
+
A boolean that is true if DICOM object was written successfully, and false if not.
|
49
|
+
:errors
|
50
|
+
An array holding any error messages, warnings or notices that have been logged
|
51
|
+
during your interaction with DObject.
|
52
|
+
:modality
|
53
|
+
A string which holds the description of the modality of the DICOM object that has been read.
|
54
|
+
Example of use:
|
55
|
+
obj = DICOM::DObject.new("myFile.dcm")
|
56
|
+
if obj.read_success
|
57
|
+
puts obj.modality
|
58
|
+
end
|
59
|
+
The following accessors are the arrays that hold all the information gathered on the DICOM object.
|
60
|
+
As such, their length is equal to the number of tags in a DICOM object.
|
61
|
+
:names
|
62
|
+
:tags
|
63
|
+
:types
|
64
|
+
:lengths
|
65
|
+
:values
|
66
|
+
:raw
|
67
|
+
:levels
|
68
68
|
|
69
69
|
PUBLIC INSTANCE METHODS
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
71
|
+
children(element, options={})
|
72
|
+
Returns the positions of all data elements inside the hierarchy of a sequence or an item.
|
73
|
+
This is useful if you later want to get the position(s) of a certain element,
|
74
|
+
restricted to the positions inside the given sequence or item.
|
75
|
+
Element may be an array index, element name or element tag.
|
76
|
+
Example 1: (Return all element positions that is contained in the following sequence)
|
77
|
+
pos = obj.children("3006,0082")
|
78
|
+
Example 2: (Return all element positions that is contained only directly beneath the following sequence)
|
79
|
+
pos = obj.children("3006,0082", :next_only => true)
|
80
|
+
|
81
|
+
get_frames
|
82
|
+
Returns the number of frames present in the image data in the DICOM file.
|
83
|
+
|
84
|
+
get_image_magick
|
85
|
+
Returns an array of RMagick image objects, where the size of the array corresponds
|
86
|
+
with the number of frames in the image data.
|
87
|
+
To call this method the user needs to have loaded the RMagick bindings in advance (require 'RMagick').
|
88
|
+
Example (retrieve object and display first frame):
|
89
|
+
require 'RMagick'
|
90
|
+
data = dicom.get_image_magick()
|
91
|
+
data[0].display
|
92
|
+
|
93
|
+
get_image_narray
|
94
|
+
Returns a 3d NArray object where the array dimensions are related to [frames, columns, rows].
|
95
|
+
To call this method the user needs to have loaded the NArray library in advance (require 'narray').
|
96
|
+
Example (retrieve object and display first frame):
|
97
|
+
require 'narray'
|
98
|
+
require 'nimage'
|
99
|
+
data = obj.get_image_narray()
|
100
|
+
NImage.show data[0,true,true]
|
101
|
+
|
102
|
+
get_image_pos
|
103
|
+
Returns the index(es) of the data element(s) that contain image data.
|
104
|
+
|
105
|
+
get_pos(string, options={})
|
106
|
+
Returns the index(es) of the data element(s) in the DICOM file that match
|
107
|
+
the supplied element tag or name.
|
108
|
+
Example 1: (Find all occurences of the specified tag in the object)
|
109
|
+
pos = obj.get_pos("3006,0080")
|
110
|
+
Example 2: (Find all occurences of the specified tag inside the specified sequence)
|
111
|
+
selection = obj.children("3006,0082")
|
112
|
+
pos = obj.get_pos("3006,0080", :array => selection)
|
113
|
+
Example 3: (Using the :partial argument to find position of all elements containing a specific string)
|
114
|
+
pos = obj.get_pos("0010", :partial => true)
|
115
|
+
pos = obj.get_pos("Name", :partial => true)
|
116
|
+
|
117
|
+
get_raw(element, options={})
|
118
|
+
Returns the raw data of the requested DICOM data element.
|
119
|
+
Element may be an array index, element name or element tag.
|
120
|
+
If you wish to return multiple data values of a tag that occurs several times in the file,
|
121
|
+
use the keyword :array => true .
|
122
|
+
Example: (Returns all data values in a array)
|
123
|
+
contour_data = obj.get_raw("3006,0050", :array => true)
|
124
|
+
|
125
|
+
get_value(element)
|
126
|
+
Returns the value (processed raw data) of the requested DICOM data element.
|
127
|
+
Element may be an array index, element name or element tag.
|
128
|
+
If you wish to return multiple values of a tag that occurs several times in the file,
|
129
|
+
use the keyword :array => true .
|
130
|
+
Example: (Returns all data values in a array)
|
131
|
+
contour_data = obj.get_value("3006,0050", :array => true)
|
132
|
+
|
133
|
+
image_to_file(file)
|
134
|
+
Dumps the pixel data of the DICOM object directly to the specified file.
|
135
|
+
This is useful if you wish to extract this data to process it with another program.
|
136
|
+
|
137
|
+
parents(element)
|
138
|
+
Returns the positions of all parents of this tag in the hierarchy.
|
139
|
+
This is useful if you want to know the position of the items or sequence tags that 'hold' your tag.
|
140
|
+
Element may be an array index, element name or element tag.
|
141
|
+
Example:
|
142
|
+
pos = obj.parents("300C,0006")
|
143
|
+
|
144
|
+
print(pos, options={})
|
145
|
+
Prints the information gathered on one or several/all tag(s) in the DICOM object:
|
146
|
+
(index, [hierarchy level,] label, name, type, length, value)
|
147
|
+
The method can print to both screen or to a text file. If print to file is chosen,
|
148
|
+
the text file will be put in the folder of the original DICOM file with a '.txt' extension.
|
149
|
+
The argument pos may be a number (array position), an array of numbers, or true.
|
150
|
+
Example 1: (Print all tags to file, with both tree visualization and level numbers)
|
151
|
+
obj.print(true, :levels => true, :tree => true, :file => true)
|
152
|
+
Example 2: (Print an array of tags to screen, no level or tree visualization)
|
153
|
+
obj.print([4,5,6])
|
154
|
+
|
155
|
+
print_all
|
156
|
+
Prints information of all tags stored in the DICOM object to the screen.
|
157
|
+
|
158
|
+
print_properties
|
159
|
+
Prints the key structural properties of the DICOM file to the screen.
|
160
|
+
|
161
|
+
remove(element)
|
162
|
+
Removes the specified data element from the DICOM object. You can use this method
|
163
|
+
if you are editing a DICOM object and wants to get rid of one or more elements.
|
164
|
+
Element may be an array index, element name or element tag.
|
165
|
+
|
166
|
+
set_value(value, element, options={})
|
167
|
+
This method can be used both to edit an existing data element, or to create
|
168
|
+
new data elements in your DICOM object.
|
169
|
+
Element may be an array index, element name or element tag.
|
170
|
+
Example 1: (Edit patient name)
|
171
|
+
obj.set_value("Anonymous", "0010,0010", :create => false)
|
172
|
+
Example 2: (Insert binary data for a specific tag)
|
173
|
+
obj.set_value(data, 52, :bin => true, :create => false)
|
174
|
+
|
175
|
+
set_image_magick(object)
|
176
|
+
Inserts a RMagick image object to the pixel data tag of your DICOM object.
|
177
|
+
|
178
|
+
set_image_file(file)
|
179
|
+
Inserts the binary content of a file to the Pixel Data tag in your DICOM object.
|
180
|
+
This can be useful if you have processed some image data using a custom program
|
181
|
+
and just wants to put that data back into a DICOM object.
|
182
|
+
|
183
|
+
write(file)
|
184
|
+
Writes the DICOM object to the specified file.
|
185
|
+
Example:
|
186
|
+
obj.write(myPath + "test_file.dcm")
|
187
187
|
|
188
188
|
|
189
189
|
CLASS Anonymizer
|
190
190
|
|
191
191
|
PUBLIC CLASS METHODS
|
192
192
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
193
|
+
new()
|
194
|
+
Initialize a new Anonymizer instance.
|
195
|
+
Example:
|
196
|
+
a = DICOM::Anonymizer.new
|
197
197
|
|
198
198
|
ACCESSORS (Read & write)
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
199
|
+
:blank
|
200
|
+
A boolean that you can set if you want to all anonymization tags to be blank
|
201
|
+
instead of having some generic value.
|
202
|
+
:enumeration
|
203
|
+
A boolean that if set will make the script set enumerated values on anonymized tags,
|
204
|
+
such that you are able to separate the DICOM files of unique individuals after anonymization.
|
205
|
+
Example of fictious result:
|
206
|
+
"Joe Sixpack" => "Person1" and "Joe Schmoe" => "Person2"
|
207
|
+
:identity_file
|
208
|
+
If you request enumeration, you can specify an identity file which will enable you to reidentify
|
209
|
+
the anonymized DICOM files at a later stage. The relationship between original names and
|
210
|
+
enumerated values is stored in a text file which you can keep for yourself, while handing out
|
211
|
+
the anonymized DICOM files to a third party.
|
212
|
+
|
213
|
+
:write_path
|
214
|
+
You may set a different path for where the anonymized DICOM files will be stored. If this
|
215
|
+
value is not set, the Anonymizer script will overwrite the old DICOM files.
|
216
|
+
Example:
|
217
|
+
a.write_path = "C:/temp/"
|
218
218
|
|
219
219
|
PUBLIC INSTANCE METHODS
|
220
220
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
221
|
+
add_folder(path)
|
222
|
+
Adds a folder who's files (including all files in subfolders) will be anonymized.
|
223
|
+
Example:
|
224
|
+
a.add_folder("/home/dicom")
|
225
225
|
|
226
|
-
|
227
|
-
|
226
|
+
add_exception(path)
|
227
|
+
Adds a folder who's files (including all files in its subfolders) will be excluded from anonymization.
|
228
228
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
229
|
+
add_tag(tag, options={})
|
230
|
+
Adds a tag to the list of tags that will be anonymized. As options you can specify value to be used
|
231
|
+
and whether the tag should be included for enumeration if this feature has been activated.
|
232
|
+
Example:
|
233
|
+
a.add_tag("0010,0010, :value => "MrAnonymous", :enum => true)
|
234
234
|
|
235
|
-
|
236
|
-
|
237
|
-
|
235
|
+
change_enum(tag, status)
|
236
|
+
Sets enumeration status for a specific tag. Status = true means the selected tag will get
|
237
|
+
an enumerated value, false means it will not.
|
238
238
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
239
|
+
execute(verbose)
|
240
|
+
Executes the anonymization process. Run this method when you are finished choosing all your settings.
|
241
|
+
Verbose (=true/false) will apply to the read/update/write process that takes place in DObject, and not
|
242
|
+
the messages of the Anonymization script itself.
|
243
243
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
244
|
+
print
|
245
|
+
Prints the list of tags that have been selected for anonymization, along with the values
|
246
|
+
that the original tags will be replaced with. If enumeration is selected, this method will also
|
247
|
+
print which tags have been selected for enumeration.
|
248
248
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
remove_tag(tag)
|
250
|
+
Removes a tag from the list of tags that will be anonymized.
|
251
|
+
Example:
|
252
|
+
a.remove_tag("0010,0010")
|
253
253
|
|
254
254
|
|
255
255
|
CLASS DClient
|
@@ -289,7 +289,7 @@ ACCESSORS (Read only)
|
|
289
289
|
PUBLIC INSTANCE METHODS
|
290
290
|
|
291
291
|
get_image(path, options={})
|
292
|
-
Retrieve a DICOM image file from a server (C-GET-RQ) (this method is
|
292
|
+
Retrieve a DICOM image file from a server (C-GET-RQ) (this method is untested (might not work)).
|
293
293
|
Accepted options:
|
294
294
|
"0008,0018" (SOP Instance UID)
|
295
295
|
"0008,0052" (Query/Retrieve Level)
|
@@ -307,7 +307,7 @@ PUBLIC INSTANCE METHODS
|
|
307
307
|
"0020,000E" (Series Instance UID)
|
308
308
|
"0020,0013" (Instance Number)
|
309
309
|
Example:
|
310
|
-
find_images("0010,0020" => patient_id, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
310
|
+
result = node.find_images("0010,0020" => patient_id, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
311
311
|
|
312
312
|
find_patients(options={})
|
313
313
|
Query a server for patients that matches your specified criteria.
|
@@ -318,7 +318,7 @@ PUBLIC INSTANCE METHODS
|
|
318
318
|
"0010,0030" (Patient's Birth Date)
|
319
319
|
"0010,0040" (Patient's Sex)
|
320
320
|
Example:
|
321
|
-
find_patients("0010,0010" => "James*")
|
321
|
+
result = node.find_patients("0010,0010" => "James*")
|
322
322
|
|
323
323
|
find_series(options={})
|
324
324
|
Query a server for series that matches your specified criteria.
|
@@ -330,7 +330,7 @@ PUBLIC INSTANCE METHODS
|
|
330
330
|
"0020,000E" (Series Instance UID)
|
331
331
|
"0020,0011" (Series Number)
|
332
332
|
Example:
|
333
|
-
find_series("0010,0020" => patient_id, "0020,000D" => study_uid)
|
333
|
+
result = node.find_series("0010,0020" => patient_id, "0020,000D" => study_uid)
|
334
334
|
|
335
335
|
find_studies(options={})
|
336
336
|
Query a server for studies that matches your specified criteria.
|
@@ -349,7 +349,7 @@ PUBLIC INSTANCE METHODS
|
|
349
349
|
"0020,000D" (Study Instance UID)
|
350
350
|
"0020,0010" (Study ID)
|
351
351
|
Example:
|
352
|
-
find_studies("0008,0020" => study_date, "0010,000D" => patient_id)
|
352
|
+
result = node.find_studies("0008,0020" => study_date, "0010,000D" => patient_id)
|
353
353
|
|
354
354
|
move_image(destination, options={})
|
355
355
|
Move an image to a dicom node other than yourself.
|
@@ -359,7 +359,7 @@ PUBLIC INSTANCE METHODS
|
|
359
359
|
"0020,000D" (Study Instance UID)
|
360
360
|
"0020,000E" (Series Instance UID)
|
361
361
|
Example:
|
362
|
-
move_image("MYDICOM", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
362
|
+
node.move_image("MYDICOM", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
363
363
|
|
364
364
|
move_study(destination, options={})
|
365
365
|
Move an entire study to a dicom node other than yourself.
|
@@ -368,12 +368,12 @@ PUBLIC INSTANCE METHODS
|
|
368
368
|
"0010,0020" (Patient ID)
|
369
369
|
"0020,000D" (Study Instance UID)
|
370
370
|
Example:
|
371
|
-
move_study("MYDICOM", "0010,0020" => patient_id, "0020,000D" => study_uid)
|
371
|
+
node.move_study("MYDICOM", "0010,0020" => patient_id, "0020,000D" => study_uid)
|
372
372
|
|
373
373
|
send(file_path)
|
374
374
|
Send a DICOM file to a service class provider (SCP/PACS).
|
375
375
|
Example:
|
376
|
-
send("myFile.dcm")
|
376
|
+
node.send("myFile.dcm")
|
377
377
|
|
378
378
|
test
|
379
379
|
Tests a connection with your DICOM server by trying a simple association.
|
@@ -386,7 +386,7 @@ PUBLIC CLASS METHODS
|
|
386
386
|
new(port, options={})
|
387
387
|
Initialize a new DServer instance.
|
388
388
|
Example:
|
389
|
-
|
389
|
+
server = DICOM::DServer.new(104)
|
390
390
|
|
391
391
|
ACCESSORS (Read & write)
|
392
392
|
:host_ae
|
@@ -420,4 +420,4 @@ PUBLIC INSTANCE METHODS
|
|
420
420
|
Completely clears the list of syntaxes that the server instance will accept.
|
421
421
|
|
422
422
|
start_scp(path)
|
423
|
-
Launch the simple storage server (Storage Content Provider - SCP)
|
423
|
+
Launch the simple storage server (Storage Content Provider - SCP).
|
data/lib/DClient.rb
CHANGED
@@ -42,20 +42,6 @@ module DICOM
|
|
42
42
|
end
|
43
43
|
|
44
44
|
|
45
|
-
# Retrieve a dicom file from a service class provider (SCP/PACS).
|
46
|
-
# Example: get_image("c:/dicom/", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
47
|
-
def get_image(path, options={})
|
48
|
-
# Study Root Query/Retrieve Information Model - GET:
|
49
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.3"
|
50
|
-
# Transfer the current options to the data_elements hash:
|
51
|
-
set_command_fragment_get
|
52
|
-
# Prepare data elements for this operation:
|
53
|
-
set_data_fragment_get_image
|
54
|
-
set_data_options(options)
|
55
|
-
perform_get(path)
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
45
|
# Query a service class provider for images that match the specified criteria.
|
60
46
|
# Example: find_images("0010,0020" => "123456789", "0020,000D" => "1.2.840.1145.342", "0020,000E" => "1.3.6.1.4.1.2452.6.687844") # (Patient ID, Study Instance UID & Series Instance UID)
|
61
47
|
def find_images(options={})
|
@@ -65,6 +51,7 @@ module DICOM
|
|
65
51
|
set_data_fragment_find_images
|
66
52
|
set_data_options(options)
|
67
53
|
perform_find
|
54
|
+
return @data_results
|
68
55
|
end
|
69
56
|
|
70
57
|
|
@@ -77,6 +64,7 @@ module DICOM
|
|
77
64
|
set_data_fragment_find_patients
|
78
65
|
set_data_options(options)
|
79
66
|
perform_find
|
67
|
+
return @data_results
|
80
68
|
end
|
81
69
|
|
82
70
|
|
@@ -89,6 +77,7 @@ module DICOM
|
|
89
77
|
set_data_fragment_find_series
|
90
78
|
set_data_options(options)
|
91
79
|
perform_find
|
80
|
+
return @data_results
|
92
81
|
end
|
93
82
|
|
94
83
|
|
@@ -101,6 +90,21 @@ module DICOM
|
|
101
90
|
set_data_fragment_find_studies
|
102
91
|
set_data_options(options)
|
103
92
|
perform_find
|
93
|
+
return @data_results
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Retrieve a dicom file from a service class provider (SCP/PACS).
|
98
|
+
# Example: get_image("c:/dicom/", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
99
|
+
def get_image(path, options={})
|
100
|
+
# Study Root Query/Retrieve Information Model - GET:
|
101
|
+
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.3"
|
102
|
+
# Transfer the current options to the data_elements hash:
|
103
|
+
set_command_fragment_get
|
104
|
+
# Prepare data elements for this operation:
|
105
|
+
set_data_fragment_get_image
|
106
|
+
set_data_options(options)
|
107
|
+
perform_get(path)
|
104
108
|
end
|
105
109
|
|
106
110
|
|
data/lib/DLibrary.rb
CHANGED
@@ -26,6 +26,36 @@ module DICOM
|
|
26
26
|
end
|
27
27
|
|
28
28
|
|
29
|
+
# Checks whether a given string is a valid transfer syntax or not.
|
30
|
+
def check_ts_validity(uid)
|
31
|
+
result = false
|
32
|
+
value = @uid[uid.rstrip]
|
33
|
+
if value != nil
|
34
|
+
if value[1] == "Transfer Syntax"
|
35
|
+
# Proved valid:
|
36
|
+
result = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return result
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Checks if the supplied transfer syntax indicates the presence of pixel compression or not.
|
44
|
+
def get_compression(uid)
|
45
|
+
result = false
|
46
|
+
if uid
|
47
|
+
value = @uid[uid.rstrip]
|
48
|
+
if value != nil
|
49
|
+
if value[1] == "Transfer Syntax" and not value[0].include?("Endian")
|
50
|
+
# It seems we have compression:
|
51
|
+
result = true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return result
|
56
|
+
end
|
57
|
+
|
58
|
+
|
29
59
|
# Returns data element name and value representation from the dictionary if the data element
|
30
60
|
# is recognized, else it returns "Unknown Name" and "UN".
|
31
61
|
def get_name_vr(tag)
|
@@ -91,20 +121,6 @@ module DICOM
|
|
91
121
|
end
|
92
122
|
|
93
123
|
|
94
|
-
# Checks whether a given string is a valid transfer syntax or not.
|
95
|
-
def check_ts_validity(uid)
|
96
|
-
result = false
|
97
|
-
value = @uid[uid.rstrip]
|
98
|
-
if value != nil
|
99
|
-
if value[1] == "Transfer Syntax"
|
100
|
-
# Proved valid:
|
101
|
-
result = true
|
102
|
-
end
|
103
|
-
end
|
104
|
-
return result
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
124
|
# Returns the name/description corresponding to a given UID.
|
109
125
|
def get_uid(uid)
|
110
126
|
value = @uid[uid.rstrip]
|
@@ -118,22 +134,6 @@ module DICOM
|
|
118
134
|
end
|
119
135
|
|
120
136
|
|
121
|
-
# Checks if the supplied transfer syntax indicates the presence of pixel compression or not.
|
122
|
-
def get_compression(uid)
|
123
|
-
result = false
|
124
|
-
if uid
|
125
|
-
value = @uid[uid.rstrip]
|
126
|
-
if value != nil
|
127
|
-
if value[1] == "Transfer Syntax" and not value[0].include?("Endian")
|
128
|
-
# It seems we have compression:
|
129
|
-
res = true
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
return result
|
134
|
-
end
|
135
|
-
|
136
|
-
|
137
137
|
# Checks the Transfer Syntax UID and return the encoding settings associated with this value.
|
138
138
|
def process_transfer_syntax(value)
|
139
139
|
valid = check_ts_validity(value)
|
@@ -160,8 +160,8 @@ module DICOM
|
|
160
160
|
# For everything else, assume compressed pixel data, with Explicit VR, Little Endian:
|
161
161
|
explicit = true
|
162
162
|
endian = false
|
163
|
-
|
164
|
-
|
163
|
+
end
|
164
|
+
return [valid, explicit, endian]
|
165
165
|
end
|
166
166
|
|
167
167
|
|
data/lib/DObject.rb
CHANGED
@@ -24,11 +24,11 @@
|
|
24
24
|
# -Support for color image data to get_image_narray and get_image_magick.
|
25
25
|
# -Complete support for Big endian (basic support is already featured).
|
26
26
|
# -Complete support for multiple frame image data to NArray and RMagick objects (partial support already featured).
|
27
|
-
# -
|
27
|
+
# -Make the image handling more intelligent with respect to interpreting data elements that hold information on the image and its properties.
|
28
28
|
|
29
29
|
module DICOM
|
30
30
|
|
31
|
-
# Class for
|
31
|
+
# Class for interacting with the DICOM object.
|
32
32
|
class DObject
|
33
33
|
|
34
34
|
attr_reader :read_success, :write_success, :modality, :errors, :segments,
|
@@ -172,9 +172,9 @@ module DICOM
|
|
172
172
|
add_msg("Error: Method read_image_magick does not have enough data available to build an image object.")
|
173
173
|
return false
|
174
174
|
end
|
175
|
-
|
175
|
+
unless @compression
|
176
176
|
# Non-compressed, just return the array contained on the particular element:
|
177
|
-
image_data=get_pixels(pos)
|
177
|
+
image_data = get_pixels(pos)
|
178
178
|
image = Magick::Image.new(columns,rows)
|
179
179
|
image.import_pixels(0, 0, columns, rows, "I", image_data)
|
180
180
|
return image
|
@@ -192,7 +192,7 @@ module DICOM
|
|
192
192
|
|
193
193
|
|
194
194
|
# Returns a 3d NArray object where the array dimensions are related to [frames, columns, rows].
|
195
|
-
|
195
|
+
# To call this method the user needs to have performed " require 'narray' " in advance.
|
196
196
|
def get_image_narray
|
197
197
|
# Does pixel data exist at all in the DICOM object?
|
198
198
|
if @compression == nil
|
@@ -256,48 +256,46 @@ module DICOM
|
|
256
256
|
end # of get_image_narray
|
257
257
|
|
258
258
|
|
259
|
-
# Returns an array of RMagick image objects, where the size of the array corresponds
|
260
|
-
|
259
|
+
# Returns an array of RMagick image objects, where the size of the array corresponds to the number of frames in the image data.
|
260
|
+
# To call this method the user needs to have loaded the ImageMagick library in advance (require 'RMagick').
|
261
261
|
def get_image_magick
|
262
262
|
# Does pixel data exist at all in the DICOM object?
|
263
263
|
if @compression == nil
|
264
|
-
add_msg("It seems pixel data is not present in this DICOM object:
|
264
|
+
add_msg("It seems pixel data is not present in this DICOM object: Returning false.")
|
265
265
|
return false
|
266
266
|
end
|
267
267
|
# No support yet for color pixel data:
|
268
268
|
if @color
|
269
|
-
add_msg("Warning: Unpacking color pixel data is not supported yet for this method:
|
269
|
+
add_msg("Warning: Unpacking color pixel data is not supported yet for this method: Aborting.")
|
270
270
|
return false
|
271
271
|
end
|
272
272
|
# Gather information about the dimensions of the image data:
|
273
|
-
rows = get_value("0028,0010")
|
274
|
-
columns = get_value("0028,0011")
|
273
|
+
rows = get_value("0028,0010", :array => true)
|
274
|
+
columns = get_value("0028,0011", :array => true)
|
275
|
+
rows = [rows] unless rows.is_a?(Array)
|
276
|
+
columns = [columns] unless columns.is_a?(Array)
|
275
277
|
frames = get_frames
|
276
278
|
image_pos = get_image_pos
|
277
279
|
# Array that will hold the RMagick image objects, one image object for each frame:
|
278
280
|
image_arr = Array.new(frames)
|
281
|
+
# A hack for the special case (some MR files), where two images are stored (one is a smaller thumbnail image):
|
282
|
+
if image_pos.length > 1 and columns.length > 1
|
283
|
+
image_pos = [image_pos.last]
|
284
|
+
columns = [columns[0]]
|
285
|
+
rows = [rows[0]]
|
286
|
+
end
|
279
287
|
# Handling of image data will depend on whether we have one or more frames,
|
280
288
|
if image_pos.size == 1
|
281
|
-
# All of the image data is located in one element:
|
282
|
-
#image_data = get_image_data(image_pos[0])
|
283
|
-
#(0..frames-1).each do |i|
|
284
|
-
# image = Magick::Image.new(columns,rows)
|
285
|
-
# image.import_pixels(0, 0, columns, rows, "I", image_data)
|
286
|
-
# image_arr[i] = image
|
287
|
-
#end
|
289
|
+
# All of the image data is located in one data element:
|
288
290
|
if frames > 1
|
289
291
|
add_msg("Unfortunately, this method only supports reading the first image frame as of now.")
|
290
292
|
end
|
291
|
-
image = read_image_magick(image_pos[0], columns, rows)
|
293
|
+
image = read_image_magick(image_pos[0], columns[0], rows[0])
|
292
294
|
image_arr[0] = image
|
293
|
-
#image_arr[i] = image
|
294
295
|
else
|
295
296
|
# Image data is encapsulated in items:
|
296
297
|
(0..frames-1).each do |i|
|
297
|
-
|
298
|
-
#image = Magick::Image.new(columns,rows)
|
299
|
-
#image.import_pixels(0, 0, columns, rows, "I", image_data)
|
300
|
-
image = read_image_magick(image_pos[i], columns, rows)
|
298
|
+
image = read_image_magick(image_pos[i], columns[0], rows[0])
|
301
299
|
image_arr[i] = image
|
302
300
|
end
|
303
301
|
end
|
@@ -307,11 +305,9 @@ module DICOM
|
|
307
305
|
|
308
306
|
# Returns the number of frames present in the image data in the DICOM file.
|
309
307
|
def get_frames
|
310
|
-
frames = get_value("0028,0008")
|
311
|
-
|
312
|
-
|
313
|
-
frames = 1
|
314
|
-
end
|
308
|
+
frames = get_value("0028,0008", :silent => true)
|
309
|
+
# If the DICOM object does not specify the number of frames explicitly, assume 1 image frame:
|
310
|
+
frames = 1 unless frames
|
315
311
|
return frames.to_i
|
316
312
|
end
|
317
313
|
|
@@ -320,10 +316,11 @@ module DICOM
|
|
320
316
|
def get_pixels(pos)
|
321
317
|
pixels = false
|
322
318
|
# We need to know what kind of bith depth the pixel data is saved with:
|
323
|
-
bit_depth = get_value("0028,0100")
|
324
|
-
|
319
|
+
bit_depth = get_value("0028,0100", :array => true)
|
320
|
+
unless bit_depth == false
|
325
321
|
# Load the binary pixel data to the Stream instance:
|
326
322
|
@stream.set_string(get_raw(pos))
|
323
|
+
bit_depth = bit_depth.first if bit_depth.is_a?(Array)
|
327
324
|
# Number of bytes used per pixel will determine how to unpack this:
|
328
325
|
case bit_depth
|
329
326
|
when 8
|
@@ -774,7 +771,8 @@ module DICOM
|
|
774
771
|
compression = "No"
|
775
772
|
end
|
776
773
|
# Bits per pixel (allocated):
|
777
|
-
bits = get_value("0028,0100")
|
774
|
+
bits = get_value("0028,0100", :array => true)
|
775
|
+
bits = bits[0].to_s if bits
|
778
776
|
# Print the file properties:
|
779
777
|
puts "Key properties of DICOM object:"
|
780
778
|
puts "-------------------------------"
|
@@ -890,7 +888,7 @@ module DICOM
|
|
890
888
|
add_msg("Warning: Method set_value could not create data element, either because data element name was not recognized in the library, or data element tag is invalid (Expected format of tags is 'GGGG,EEEE').")
|
891
889
|
else
|
892
890
|
# As we wish to create a new data element, we need to find out where to insert it in the element arrays:
|
893
|
-
# We will do this by finding the
|
891
|
+
# We will do this by finding the array position of the last element that will (alphabetically/numerically) stay in front of this element.
|
894
892
|
if @tags.size > 0
|
895
893
|
# Search the array:
|
896
894
|
index = -1
|
@@ -898,7 +896,7 @@ module DICOM
|
|
898
896
|
while quit != true do
|
899
897
|
if index+1 >= @tags.length # We have reached end of array.
|
900
898
|
quit = true
|
901
|
-
elsif tag < @tags[index+1] # We are past the correct position.
|
899
|
+
elsif tag < @tags[index+1] and @levels[index+1] == 0 # We are past the correct position (only match against top level tags).
|
902
900
|
quit = true
|
903
901
|
else # Increase index in anticipation of a 'hit'.
|
904
902
|
index += 1
|
@@ -1005,19 +1003,21 @@ module DICOM
|
|
1005
1003
|
end # of create_element
|
1006
1004
|
|
1007
1005
|
|
1008
|
-
# Encodes a value to binary (used for inserting values
|
1006
|
+
# Encodes a value to binary (used for inserting values into a DICOM object).
|
1009
1007
|
def encode(value, vr)
|
1010
|
-
# Our value needs to be inside an array to be encoded:
|
1011
|
-
value = [value] if not value.is_a?(Array)
|
1012
1008
|
# VR will decide how to encode this value:
|
1013
1009
|
case vr
|
1014
1010
|
when "AT" # (Data element tag: Assume it has the format "GGGG,EEEE"
|
1015
|
-
|
1011
|
+
if value.is_a_tag?
|
1012
|
+
bin = @stream.encode_tag(value)
|
1013
|
+
else
|
1014
|
+
add_msg("Invalid tag format (#{value}). Expected format: 'GGGG,EEEE'")
|
1015
|
+
end
|
1016
1016
|
# We have a number of VRs that are encoded as string:
|
1017
1017
|
when 'AE','AS','CS','DA','DS','DT','IS','LO','LT','PN','SH','ST','TM','UI','UT'
|
1018
1018
|
# In case we are dealing with a number string element, the supplied value might be a number
|
1019
1019
|
# instead of a string, and as such, we convert to string just to make sure this will work nicely:
|
1020
|
-
value
|
1020
|
+
value = value.to_s
|
1021
1021
|
bin = @stream.encode_value(value, "STR")
|
1022
1022
|
# Image related value representations:
|
1023
1023
|
when "OW"
|
@@ -1049,6 +1049,7 @@ module DICOM
|
|
1049
1049
|
return bin
|
1050
1050
|
end # of encode
|
1051
1051
|
|
1052
|
+
|
1052
1053
|
# Modifies existing data element:
|
1053
1054
|
def modify_element(value, pos, options={})
|
1054
1055
|
bin_only = options[:bin]
|
data/lib/DRead.rb
CHANGED
File without changes
|
data/lib/DServer.rb
CHANGED
File without changes
|
data/lib/DWrite.rb
CHANGED
@@ -10,7 +10,8 @@
|
|
10
10
|
# Please contact the author if you discover any issues with file creation.
|
11
11
|
|
12
12
|
module DICOM
|
13
|
-
|
13
|
+
|
14
|
+
# Class for writing the data from a DObject to a valid DICOM file.
|
14
15
|
class DWrite
|
15
16
|
|
16
17
|
attr_writer :tags, :types, :lengths, :raw, :rest_endian, :rest_explicit
|
@@ -309,7 +310,8 @@ module DICOM
|
|
309
310
|
end
|
310
311
|
|
311
312
|
|
312
|
-
# Tests if the file is writable
|
313
|
+
# Tests if the file/path is writable, creates any folders
|
314
|
+
# if necessary, and opens the file for writing.
|
313
315
|
def open_file(file)
|
314
316
|
# Two cases: File already exists or it does not.
|
315
317
|
# Check if file exists:
|
@@ -323,22 +325,20 @@ module DICOM
|
|
323
325
|
end
|
324
326
|
else
|
325
327
|
# File does not exist.
|
326
|
-
# Check if this file's path contains a
|
327
|
-
|
328
|
-
if
|
328
|
+
# Check if this file's path contains a folder that does not exist, and therefore needs to be created:
|
329
|
+
folders = file.split(File::SEPARATOR)
|
330
|
+
if folders.length > 1
|
329
331
|
# Remove last element (which should be the file string):
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
FileUtils.mkdir_p path
|
338
|
-
end
|
332
|
+
folders.pop
|
333
|
+
path = folders.join(File::SEPARATOR)
|
334
|
+
# Check if this path exists:
|
335
|
+
unless File.directory?(path)
|
336
|
+
# We need to create (parts of) this path:
|
337
|
+
require 'fileutils'
|
338
|
+
FileUtils.mkdir_p path
|
339
339
|
end
|
340
340
|
end
|
341
|
-
# The path to this non-existing file should now be prepared, and we
|
341
|
+
# The path to this non-existing file should now be prepared, and we proceed to creating the file:
|
342
342
|
@file = File.new(file, "wb")
|
343
343
|
end
|
344
344
|
end
|
data/lib/Link.rb
CHANGED
File without changes
|
data/lib/Stream.rb
CHANGED
@@ -96,14 +96,16 @@ module DICOM
|
|
96
96
|
|
97
97
|
# Encodes content (string, number, array of numbers) and returns the binary string.
|
98
98
|
def encode(value, type)
|
99
|
-
|
99
|
+
value = [value] unless value.is_a?(Array)
|
100
|
+
return value.pack(vr_to_str(type))
|
100
101
|
end
|
101
102
|
|
102
103
|
|
103
104
|
# Encodes content (string, number, array of numbers) to a binary string and pastes it to
|
104
105
|
# the beginning of the @string variable of this instance.
|
105
106
|
def encode_first(value, type)
|
106
|
-
|
107
|
+
value = [value] unless value.is_a?(Array)
|
108
|
+
bin = value.pack(vr_to_str(type))
|
107
109
|
@string = bin + @string
|
108
110
|
end
|
109
111
|
|
@@ -111,7 +113,8 @@ module DICOM
|
|
111
113
|
# Encodes content (string, number, array of numbers) to a binary string and pastes it to
|
112
114
|
# the end of the @string variable of this instance.
|
113
115
|
def encode_last(value, type)
|
114
|
-
|
116
|
+
value = [value] unless value.is_a?(Array)
|
117
|
+
bin = value.pack(vr_to_str(type))
|
115
118
|
@string = @string + bin
|
116
119
|
end
|
117
120
|
|
data/lib/dicom.rb
CHANGED
File without changes
|
data/lib/ruby_extensions.rb
CHANGED
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dicom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christoffer Lervag
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-08-
|
12
|
+
date: 2009-08-23 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -65,6 +65,6 @@ rubyforge_project: dicom
|
|
65
65
|
rubygems_version: 1.3.3
|
66
66
|
signing_key:
|
67
67
|
specification_version: 3
|
68
|
-
summary: Library for handling DICOM files and network communication.
|
68
|
+
summary: Library for handling DICOM files and DICOM network communication.
|
69
69
|
test_files: []
|
70
70
|
|