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