metamri 0.1.13 → 0.1.14
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.
- data/.gitignore +1 -1
- data/README.rdoc +3 -1
- data/VERSION +1 -1
- data/bin/list_visit +17 -21
- data/lib/metamri/core_additions.rb +25 -2
- data/lib/metamri/raw_image_dataset_resource.rb +99 -3
- data/lib/metamri/visit_raw_data_directory_resource.rb +20 -6
- data/lib/metamri.rb +1 -1
- data/lib/nifti_builder.rb +6 -0
- data/lib/raw_image_dataset.rb +41 -3
- data/lib/visit_raw_data_directory.rb +10 -3
- data/metamri.gemspec +3 -3
- metadata +24 -13
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
== ImageData
|
2
2
|
|
3
|
-
A small library that can be used to extract metadata from large collections of research MR imaging data sets.
|
3
|
+
A small library that can be used to extract metadata from large collections of research MR imaging data sets.
|
4
|
+
|
5
|
+
Support is also provided to insert the metadata into a Wisconsin ADRC Imaging Core compatible database. Several
|
4
6
|
command line utilities are provided as well as a minimal API that is useful for building ruby on rails rake tasks.
|
5
7
|
|
6
8
|
You will most likely be interested in either:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.14
|
data/bin/list_visit
CHANGED
@@ -9,17 +9,18 @@
|
|
9
9
|
#
|
10
10
|
# == Examples
|
11
11
|
#
|
12
|
-
# list_visit /Data/vtrak1/raw/ries.aware.visit1/awr001_7854_02102009
|
12
|
+
# %> list_visit /Data/vtrak1/raw/ries.aware.visit1/awr001_7854_02102009
|
13
13
|
#
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# list_visit
|
16
16
|
#
|
17
17
|
# If no raw data directory is given, the current directory will be assumed.
|
18
|
-
#
|
18
|
+
# %> cd /Data/vtrak1/raw/ries.aware.visit1/awr001_7854_02102009
|
19
|
+
# %> list_visit
|
19
20
|
#
|
20
21
|
# == Usage
|
21
|
-
# list_visit <raw_data_directory>
|
22
|
-
#
|
22
|
+
# %> list_visit <raw_data_directory>
|
23
|
+
#
|
23
24
|
# For help use: list_vist -h
|
24
25
|
#
|
25
26
|
# == Options
|
@@ -38,18 +39,19 @@ require 'rdoc/usage'
|
|
38
39
|
# require 'logger'
|
39
40
|
|
40
41
|
def list_visit(raw_directory)
|
41
|
-
|
42
|
-
|
42
|
+
unless $LOG
|
43
|
+
$LOG = Logger.new(STDOUT)
|
44
|
+
$LOG.level = Logger::INFO
|
45
|
+
end
|
43
46
|
|
44
47
|
# First try to lookup Visit and Datasets through active_resource
|
45
|
-
# Then fall back to scanning them fresh.
|
48
|
+
# Then fall back to scanning them fresh using the metamri classes.
|
46
49
|
|
47
50
|
begin
|
48
|
-
# raise
|
49
51
|
visit = VisitRawDataDirectoryResource.find(:first, :params => {:search => {:path => raw_directory}})
|
50
|
-
raise "Could not lookup visit using path." unless visit
|
51
|
-
raise "
|
52
|
-
rescue
|
52
|
+
raise IOError.new("Could not lookup visit using path.") unless visit
|
53
|
+
raise IOError.new("Returned visit does not match path.") unless visit.path == raw_directory
|
54
|
+
rescue IOError => e
|
53
55
|
puts e
|
54
56
|
visit = VisitRawDataDirectory.new(raw_directory)
|
55
57
|
begin
|
@@ -60,15 +62,9 @@ def list_visit(raw_directory)
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# $LOG.error "Exception message: #{e.message}"
|
67
|
-
# raise e
|
68
|
-
# end
|
69
|
-
|
70
|
-
# Visit is Either a RawVisitDataDirectory or a RawVisitDataDirectoryResource
|
71
|
-
# Either can handle printing to_s
|
65
|
+
# Output a pretty version of the visit details.
|
66
|
+
# Visit is Either a RawVisitDataDirectory or a RawVisitDataDirectoryResource,
|
67
|
+
# and either should respond to to_s
|
72
68
|
visit.to_s
|
73
69
|
|
74
70
|
end
|
@@ -48,6 +48,10 @@ class Pathname
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
# def first_pfile(&block)
|
52
|
+
# Pathname.new(filename).local_copy { block }
|
53
|
+
# end
|
54
|
+
|
51
55
|
def first_dicom
|
52
56
|
entries.each do |leaf|
|
53
57
|
branch = self + leaf
|
@@ -87,7 +91,14 @@ class Pathname
|
|
87
91
|
return
|
88
92
|
end
|
89
93
|
|
90
|
-
|
94
|
+
=begin
|
95
|
+
Creates a local, unzipped copy of a file for use in scanning.
|
96
|
+
Will return a pathname to the local copy if called directly, or can also be
|
97
|
+
passed a block. If it is passed a block, it will create the local copy
|
98
|
+
and ensure the local copy is deleted.
|
99
|
+
=end
|
100
|
+
|
101
|
+
def local_copy(tempdir = Dir.tmpdir, &block)
|
91
102
|
tfbase = self.to_s =~ /\.bz2$/ ? self.basename.to_s.chomp(".bz2") : self.basename.to_s
|
92
103
|
tfbase.escape_filename
|
93
104
|
tmpfile = File.join(tempdir, tfbase)
|
@@ -96,7 +107,19 @@ class Pathname
|
|
96
107
|
else
|
97
108
|
FileUtils.cp(self.to_s, tmpfile)
|
98
109
|
end
|
99
|
-
|
110
|
+
|
111
|
+
lc = Pathname.new(tmpfile)
|
112
|
+
|
113
|
+
if block
|
114
|
+
begin
|
115
|
+
yield lc
|
116
|
+
ensure
|
117
|
+
lc.delete
|
118
|
+
end
|
119
|
+
|
120
|
+
else
|
121
|
+
return lc
|
122
|
+
end
|
100
123
|
end
|
101
124
|
|
102
125
|
end
|
@@ -9,10 +9,39 @@ class RawImageDatasetResource < ActiveResource::Base
|
|
9
9
|
# so check the current schema.rb file for those.
|
10
10
|
def to_metamri_raw_image_dataset
|
11
11
|
# A Metamri Class requires at least one valid image file.
|
12
|
-
# This is a little
|
12
|
+
# This is a little wasteful since we really only care about the variables,
|
13
|
+
# not rescanning them.
|
13
14
|
|
14
|
-
Pathname.new(path)
|
15
|
-
|
15
|
+
filename = Pathname.new(File.join(path, scanned_file))
|
16
|
+
filename_matches = /P\d{5}.7(.bz2)?/.match(filename)
|
17
|
+
|
18
|
+
if filename_matches # Pfile
|
19
|
+
if filename_matches[1] # '.bz2' if present, nil if otherwise.
|
20
|
+
filename = Pathname.new(File.join(filename, '.bz2'))
|
21
|
+
end
|
22
|
+
|
23
|
+
# The scanned file is always reported in unzipped format, so we don't
|
24
|
+
# have to worry about stripping a .bz2 extension.
|
25
|
+
# The actual file on the filesystem may be zipped or unzipped
|
26
|
+
# (although it Should! be zipped. Check for that or return IOError.
|
27
|
+
zipped_filename = filename.to_s.chomp + '.bz2'
|
28
|
+
|
29
|
+
if filename.file?
|
30
|
+
image_file = filename
|
31
|
+
elsif Pathname.new(zipped_filename).file?
|
32
|
+
image_file = Pathname.new(zipped_filename)
|
33
|
+
else
|
34
|
+
raise IOError, "Could not find #{filename} or it's bz2 zipped equivalent #{zipped_filename}."
|
35
|
+
end
|
36
|
+
|
37
|
+
image_file.local_copy do |local_pfile|
|
38
|
+
@dataset = RawImageDataset.new( path, [RawImageFile.new(local_pfile)])
|
39
|
+
end
|
40
|
+
|
41
|
+
else # Dicom
|
42
|
+
Pathname.new(path).first_dicom do |fd|
|
43
|
+
@dataset = RawImageDataset.new( path, [RawImageFile.new(fd)] )
|
44
|
+
end
|
16
45
|
end
|
17
46
|
|
18
47
|
return @dataset
|
@@ -29,4 +58,71 @@ class RawImageDatasetResource < ActiveResource::Base
|
|
29
58
|
# super
|
30
59
|
# end
|
31
60
|
# end
|
61
|
+
|
62
|
+
# def file_count
|
63
|
+
# unless @file_count
|
64
|
+
# if @raw_image_files.first.dicom?
|
65
|
+
# @file_count = Dir.open(@directory).reject{ |branch| /^\./.match(branch) }.length
|
66
|
+
# elsif @raw_image_files.first.pfile?
|
67
|
+
# @file_count = 1
|
68
|
+
# else raise "File not recognized as dicom or pfile."
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
# return @file_count
|
72
|
+
# end
|
73
|
+
|
74
|
+
def pfile?
|
75
|
+
scanned_file =~ /^P.*.7$/
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def file_count
|
81
|
+
if pfile?
|
82
|
+
file_count = 1
|
83
|
+
else
|
84
|
+
file_count = Dir.open(path).reject{ |branch| /(^\.|.yaml$)/.match(branch) }.length
|
85
|
+
end
|
86
|
+
return file_count
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a relative filepath to the dataset. Handles dicoms by returning the
|
90
|
+
# dataset directory, and pfiles by returning either the pfile filename or,
|
91
|
+
# if passed a visit directory, the relative path from the visit directory to
|
92
|
+
# the pfile (i.e. P00000.7 or raw/P00000.7).
|
93
|
+
def relative_dataset_path(visit_dir = nil)
|
94
|
+
if pfile?
|
95
|
+
relative_dataset_path = scanned_file
|
96
|
+
else # Then it's a dicom.
|
97
|
+
relative_dataset_path = File.basename(path)
|
98
|
+
end
|
99
|
+
|
100
|
+
return relative_dataset_path
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates an Hirb Table for pretty output of dataset info.
|
104
|
+
# It takes an array of either RawImageDatasets or RawImageDatasetResources
|
105
|
+
def self.to_table(datasets)
|
106
|
+
Hirb::Helpers::AutoTable.render(
|
107
|
+
datasets.sort_by{ |ds| [ds.timestamp, File.basename(ds.path)] },
|
108
|
+
:headers => { :relative_dataset_path => 'Dataset', :series_description => 'Series Details', :file_count => "File Count", },
|
109
|
+
:fields => [:relative_dataset_path, :series_description, :file_count],
|
110
|
+
:description => false # Turn off rendering row count description at bottom.
|
111
|
+
)
|
112
|
+
rescue NameError => e
|
113
|
+
puts e
|
114
|
+
|
115
|
+
# Header Line
|
116
|
+
printf "\t%-15s %-30s [%s]\n", "Directory", "Series Description", "Files"
|
117
|
+
|
118
|
+
# Dataset Lines
|
119
|
+
datasets.sort_by{|ds| [ds.timestamp, File.basename(ds.path)] }.each do |dataset|
|
120
|
+
printf "\t%-15s %-30s [%s]\n", dataset.relative_dataset_path, dataset.series_description, dataset.file_count
|
121
|
+
end
|
122
|
+
|
123
|
+
# Reminder Line
|
124
|
+
puts "(This would be much prettier if you installed hirb.)"
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
32
128
|
end
|
@@ -8,8 +8,9 @@ class VisitRawDataDirectoryResource < ActiveResource::Base
|
|
8
8
|
self.element_name = "visit"
|
9
9
|
|
10
10
|
# Creates a Backwards Transfer to go from ActiveRecord to Metamri Classes
|
11
|
-
#
|
12
|
-
#
|
11
|
+
#
|
12
|
+
# ActiveResource will provide :attr methods for column names from the
|
13
|
+
# database, so check the current schema.rb file for those.
|
13
14
|
def to_metamri_visit_raw_data_directory
|
14
15
|
@visit = VisitRawDataDirectory.new(path)
|
15
16
|
@visit.timestamp = date
|
@@ -23,10 +24,23 @@ class VisitRawDataDirectoryResource < ActiveResource::Base
|
|
23
24
|
@datasets ||= RawImageDatasetResource.find(:all, :from => "/visits/#{id}/image_datasets.xml" )
|
24
25
|
end
|
25
26
|
|
27
|
+
# Convert a Resource and its datasets to a VisitRawDataDirectory and
|
28
|
+
# RawImageDataset, respectively, then pretty print it using
|
29
|
+
# VisitRawDataDirectory.to_s
|
30
|
+
# def to_s
|
31
|
+
# metamri_visit = to_metamri_visit_raw_data_directory
|
32
|
+
# metamri_visit.datasets = datasets.collect { |ds| ds.to_metamri_raw_image_dataset}
|
33
|
+
# metamri_visit.to_s
|
34
|
+
# end
|
35
|
+
|
26
36
|
def to_s
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
puts; path.length.times { print "-" }; puts
|
38
|
+
puts "#{path}"
|
39
|
+
puts "#{rmr} - #{date} - #{scanner_source}"
|
40
|
+
puts
|
41
|
+
# puts "#{@scan_procedure_name}"
|
42
|
+
puts RawImageDatasetResource.to_table(datasets)
|
43
|
+
puts "Notes: " + notes unless notes.nil? or notes.empty?
|
30
44
|
end
|
45
|
+
|
31
46
|
end
|
32
|
-
|
data/lib/metamri.rb
CHANGED
data/lib/nifti_builder.rb
CHANGED
data/lib/raw_image_dataset.rb
CHANGED
@@ -179,7 +179,15 @@ have more component files than shell commands can handle.
|
|
179
179
|
|
180
180
|
|
181
181
|
def file_count
|
182
|
-
@file_count
|
182
|
+
unless @file_count
|
183
|
+
if @raw_image_files.first.dicom?
|
184
|
+
@file_count = Dir.open(@directory).reject{ |branch| /(^\.|.yaml$)/.match(branch) }.length
|
185
|
+
elsif @raw_image_files.first.pfile?
|
186
|
+
@file_count = 1
|
187
|
+
else raise "File not recognized as dicom or pfile."
|
188
|
+
end
|
189
|
+
end
|
190
|
+
return @file_count
|
183
191
|
end
|
184
192
|
|
185
193
|
# Creates an Hirb Table for pretty output of dataset info.
|
@@ -191,11 +199,41 @@ have more component files than shell commands can handle.
|
|
191
199
|
|
192
200
|
Hirb::Helpers::AutoTable.render(
|
193
201
|
datasets.sort_by{ |ds| [ds.timestamp, File.basename(ds.directory)] },
|
194
|
-
:headers => { :
|
195
|
-
:fields => [:
|
202
|
+
:headers => { :relative_dataset_path => 'Dataset', :series_details => 'Series Details', :file_count => 'File Count'},
|
203
|
+
:fields => [:relative_dataset_path, :series_details, :file_count],
|
204
|
+
:description => false # Turn off rendering row count description at bottom.
|
196
205
|
)
|
197
206
|
|
198
207
|
end
|
208
|
+
|
209
|
+
# Returns a relative filepath to the dataset. Handles dicoms by returning the
|
210
|
+
# dataset directory, and pfiles by returning either the pfile filename or,
|
211
|
+
# if passed a visit directory, the relative path from the visit directory to
|
212
|
+
# the pfile (i.e. P00000.7 or raw/P00000.7).
|
213
|
+
def relative_dataset_path(visit_dir = nil)
|
214
|
+
image_file = @raw_image_files.first
|
215
|
+
case image_file.file_type
|
216
|
+
when 'dicom'
|
217
|
+
relative_dataset_path = File.basename(directory)
|
218
|
+
when 'pfile'
|
219
|
+
full_dataset_path = Pathname.new(File.join(directory, image_file.filename))
|
220
|
+
if visit_dir
|
221
|
+
relative_dataset_path = full_dataset_path.relative_path_from(visit_dir)
|
222
|
+
else
|
223
|
+
relative_dataset_path = image_file.filename
|
224
|
+
end
|
225
|
+
else raise "Cannot identify #{@raw_image_files.first.filename}"
|
226
|
+
end
|
227
|
+
|
228
|
+
return relative_dataset_path
|
229
|
+
end
|
230
|
+
|
231
|
+
# Reports series details, including description and possilby image quality
|
232
|
+
# check comments.
|
233
|
+
def series_details
|
234
|
+
@series_description
|
235
|
+
end
|
236
|
+
|
199
237
|
private
|
200
238
|
|
201
239
|
# Gets the earliest timestamp among the raw image files in this dataset.
|
@@ -184,12 +184,19 @@ Returns an array of the created nifti files.
|
|
184
184
|
def to_s
|
185
185
|
puts; @visit_directory.length.times { print "-" }; puts
|
186
186
|
puts "#{@visit_directory}"
|
187
|
-
puts "#{@rmr_number} - #{@scanner_source}"
|
188
|
-
puts
|
189
|
-
puts "#{@scanid}"
|
187
|
+
puts "#{@rmr_number} - #{@timestamp.strftime('%F')} - #{@scanner_source}"
|
188
|
+
puts
|
190
189
|
puts RawImageDataset.to_table(@datasets)
|
191
190
|
return
|
192
191
|
rescue NameError => e
|
192
|
+
puts e
|
193
|
+
if @datasets.first.class.to_s == "RawImageDatasetResource"
|
194
|
+
@datasets = @datasets.map { |ds| ds.to_metamri_image_dataset }
|
195
|
+
end
|
196
|
+
|
197
|
+
# puts @datasets.first.class.to_s
|
198
|
+
# puts @datasets
|
199
|
+
|
193
200
|
# Header Line
|
194
201
|
printf "\t%-15s %-30s [%s]\n", "Directory", "Series Description", "Files"
|
195
202
|
|
data/metamri.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{metamri}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.14"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kristopher J. Kosmatka"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-03-11}
|
13
13
|
s.description = %q{Extraction of MRI metadata and insertion into compatible sqlite3 databases.}
|
14
14
|
s.email = %q{kk4@medicine.wisc.edu}
|
15
15
|
s.executables = ["import_study.rb", "import_visit.rb", "import_respiratory_files.rb", "list_visit", "convert_visit.rb"]
|
@@ -48,7 +48,7 @@ Gem::Specification.new do |s|
|
|
48
48
|
s.homepage = %q{http://github.com/brainmap/metamri}
|
49
49
|
s.rdoc_options = ["--charset=UTF-8"]
|
50
50
|
s.require_paths = ["lib"]
|
51
|
-
s.rubygems_version = %q{1.3.
|
51
|
+
s.rubygems_version = %q{1.3.6}
|
52
52
|
s.summary = %q{MRI metadata}
|
53
53
|
s.test_files = [
|
54
54
|
"test/nifti_builder_spec.rb",
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metamri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 14
|
9
|
+
version: 0.1.14
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Kristopher J. Kosmatka
|
@@ -9,29 +14,33 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-03-11 00:00:00 -06:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: sqlite3-ruby
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
23
29
|
version: "0"
|
24
|
-
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
25
32
|
- !ruby/object:Gem::Dependency
|
26
33
|
name: rspec
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
33
41
|
version: "0"
|
34
|
-
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
35
44
|
description: Extraction of MRI metadata and insertion into compatible sqlite3 databases.
|
36
45
|
email: kk4@medicine.wisc.edu
|
37
46
|
executables:
|
@@ -85,18 +94,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
94
|
requirements:
|
86
95
|
- - ">="
|
87
96
|
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
88
99
|
version: "0"
|
89
|
-
version:
|
90
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
101
|
requirements:
|
92
102
|
- - ">="
|
93
103
|
- !ruby/object:Gem::Version
|
104
|
+
segments:
|
105
|
+
- 0
|
94
106
|
version: "0"
|
95
|
-
version:
|
96
107
|
requirements: []
|
97
108
|
|
98
109
|
rubyforge_project:
|
99
|
-
rubygems_version: 1.3.
|
110
|
+
rubygems_version: 1.3.6
|
100
111
|
signing_key:
|
101
112
|
specification_version: 3
|
102
113
|
summary: MRI metadata
|