Skip to content

Commit 60cb703

Browse files
author
Robert D. Vincent
committed
Support non-byte volumes for MINC. Also fixes aces#259.
1 parent fb6f1ce commit 60cb703

6 files changed

Lines changed: 204 additions & 68 deletions

File tree

examples/css/volume-viewer-demo.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ canvas.slice-display {
9797
-moz-box-shadow: 1px 1px 0px 0px #787878;
9898
-webkit-box-shadow: 1px 1px 0px 0px #787878;
9999
box-shadow: 1px 1px 0px 0px #787878;
100-
width: 2em;
100+
width: 3em;
101+
font-size: 10px;
101102
}
102103

103104
.coords .control-inputs {
@@ -162,5 +163,8 @@ canvas.slice-display {
162163
}
163164

164165
.intensity-value {
165-
padding: 1px;
166+
padding: 2px;
167+
-moz-border-radius: 3px;
168+
-webkit-border-radius: 3px;
169+
border-radius: 3px;
166170
}

scripts/minc2volume-viewer.js

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* Author: Tarek Sherif <tsherif@gmail.com> (http://tareksherif.ca/)
2424
* Author: Nicolas Kassis
2525
* Author: Paul Mougel
26+
* Author: Robert D. Vincent <robert.d.vincent@mcgill.ca>
2627
*/
2728

2829
/*
@@ -44,15 +45,37 @@
4445
var fs = require("fs");
4546
var exec = require("child_process").exec;
4647
var spawn = require("child_process").spawn;
47-
var version = "0.3.1";
48+
var version = "0.3.2";
49+
50+
var datatypes = [
51+
'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'float32', 'float64'
52+
];
4853

4954
if (require.main === module) {
50-
var filename = process.argv[2];
55+
var args = process.argv.slice(2);
56+
var arg;
57+
while ((arg = args.shift()) !== undefined) {
58+
var filename;
59+
var datatype;
60+
if (arg === '-T') {
61+
datatype = args.shift();
62+
if (datatypes.indexOf(datatype) < 0) {
63+
printUsage();
64+
process.exit(0);
65+
}
66+
}
67+
else {
68+
filename = arg;
69+
}
70+
}
5171

5272
if (filename === undefined) {
5373
printUsage();
5474
process.exit(0);
5575
}
76+
if (datatype === undefined) {
77+
datatype = 'uint8';
78+
}
5679

5780
fs.exists(filename, function(exists) {
5881

@@ -73,15 +96,15 @@ if (require.main === module) {
7396
console.log("Processing file:", filename);
7497

7598
console.log("Creating header file: ", basename + ".header");
76-
getHeader(filename, function(err, header) {
99+
getHeaderWithType(filename, datatype, function(err, header) {
77100
if (err) {
78101
return logExecutionError(err);
79102
}
80103
fs.writeFile(basename + ".header", JSON.stringify(header));
81104
});
82105

83106
console.log("Creating raw data file: ", basename + ".raw");
84-
rawDataStream = getRawDataStream(filename);
107+
rawDataStream = getRawDataStream(filename, datatype);
85108
rawDataStream.on('error', logExecutionError);
86109
rawFileStream = fs.createWriteStream(basename + ".raw");
87110
rawDataStream.pipe(rawFileStream);
@@ -90,6 +113,7 @@ if (require.main === module) {
90113
});
91114
} else {
92115
module.exports.getHeader = getHeader;
116+
module.exports.getHeaderWithType = getHeaderWithType;
93117
module.exports.getRawDataStream = getRawDataStream;
94118
}
95119

@@ -99,11 +123,45 @@ if (require.main === module) {
99123

100124
function printUsage() {
101125
console.log("minc2volume-viewer.js v" + version);
102-
console.log("\nUsage: node minc2volume-viewer.js <filename>\n");
126+
console.log("Usage: node minc2volume-viewer.js [-T <datatype>] <filename>");
127+
console.log(" Where datatype is one of: ");
128+
for (var k in datatypes)
129+
console.log(" " + datatypes[k]);
103130
}
104131

105-
function getRawDataStream(filename) {
106-
var minctoraw = spawn("minctoraw", ["-byte", "-unsigned", "-normalize", filename]);
132+
function getRawDataStream(filename, datatype) {
133+
var args = ["-normalize", filename];
134+
135+
if (datatype === undefined)
136+
datatype = 'uint8';
137+
138+
switch (datatype) {
139+
case 'int8':
140+
args.unshift('-byte', '-signed');
141+
break;
142+
case 'uint8':
143+
args.unshift('-byte', '-unsigned');
144+
break;
145+
case 'int16':
146+
args.unshift('-short', '-signed');
147+
break;
148+
case 'uint16':
149+
args.unshift('-short', '-unsigned');
150+
break;
151+
case 'int32':
152+
args.unshift('-int', '-signed');
153+
break;
154+
case 'uint32':
155+
args.unshift('-int', '-unsigned');
156+
break;
157+
case 'float32':
158+
args.unshift('-float');
159+
break;
160+
case 'float64':
161+
args.unshift('-double');
162+
break;
163+
}
164+
var minctoraw = spawn("minctoraw", args);
107165
minctoraw.on("exit", function (code) {
108166
if (code === null || code !== 0) {
109167
var err = new Error("Process minctoraw failed with error code " + code);
@@ -117,7 +175,11 @@ function getRawDataStream(filename) {
117175
}
118176

119177
function getHeader(filename, callback) {
120-
178+
getHeaderWithType(filename, 'uint8', callback);
179+
}
180+
181+
function getHeaderWithType(filename, datatype, callback) {
182+
121183
function getSpace(filename, header, space, callback) {
122184
header[space] = {};
123185
exec("mincinfo -attval " + space + ":start " + filename, function(error, stdout) {
@@ -210,8 +272,10 @@ function getHeader(filename, callback) {
210272
});
211273
}
212274

213-
function buildHeader(filename, callback) {
275+
function buildHeader(filename, datatype, callback) {
214276
var header = {};
277+
278+
header.datatype = datatype;
215279

216280
getOrder(header, filename, function(err, header) {
217281
if (err) {
@@ -240,7 +304,7 @@ function getHeader(filename, callback) {
240304
});
241305
}
242306

243-
buildHeader(filename, callback);
307+
buildHeader(filename, datatype, callback);
244308
}
245309

246310
function logExecutionError(error) {

scripts/minc2volume-viewer.py

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#!/usr/bin/env python3
1+
#!/usr/bin/env python
22
#
33
# BrainBrowser: Web-based Neurological Visualization Tools
44
# (https://brainbrowser.cbrain.mcgill.ca)
55
#
6-
# Copyright (C) 2011 McGill University
6+
# Copyright (C) 2011 McGill University
77
#
88
# This program is free software: you can redistribute it and/or modify
99
# it under the terms of the GNU Affero General Public License as
@@ -19,34 +19,44 @@
1919
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2020

2121
# Author: Jon Pipitone <jon@pipitone.ca>
22+
# Author: Robert D. Vincent <robert.d.vincent@mcgill.ca>
23+
# (Made it work with Python 2.7, supported multiple voxel types)
2224

25+
from __future__ import print_function
2326
import sys
2427
import shutil
2528
import os.path
2629
import argparse
2730
import subprocess
2831
import json
2932

30-
required_minc_cmdline_tools = ['mincinfo', 'minctoraw']
33+
required_minc_cmdline_tools = ['mincinfo', 'minctoraw']
3134

32-
def console_error(message, exit_code):
35+
def console_error(message, exit_code):
3336
print(message, file=sys.stderr)
34-
sys.exit(1)
37+
sys.exit(exit_code)
3538

3639
def console_log(message):
3740
print(message)
3841

39-
def check_minc_tools_installed():
42+
def which(pgm):
43+
path=os.getenv('PATH')
44+
for p in path.split(os.path.pathsep):
45+
p=os.path.join(p, pgm)
46+
if os.path.exists(p) and os.access(p, os.X_OK):
47+
return p
48+
49+
def check_minc_tools_installed():
4050
for tool in required_minc_cmdline_tools:
41-
if not shutil.which(tool):
51+
if not which(tool):
4252
console_error(
4353
"minc2volume-viewer.py requires that the MINC tools be installed.\n"
4454
"Visit http://www.bic.mni.mcgill.ca/ServicesSoftware/MINC for info.", 1)
4555

46-
def cmd(command):
56+
def cmd(command):
4757
return subprocess.check_output(command.split(), universal_newlines=True).strip()
4858

49-
def get_space(mincfile, space):
59+
def get_space(mincfile, space):
5060
header = {
5161
"start" : float(cmd("mincinfo -attval {}:start {}".format(space,mincfile))),
5262
"space_length" : float(cmd("mincinfo -dimlength {} {}".format(space,mincfile))),
@@ -60,45 +70,63 @@ def get_space(mincfile, space):
6070

6171
return header
6272

63-
def make_header(mincfile, headerfile):
73+
def make_header(mincfile, datatype, headerfile):
6474
header = {}
6575

76+
header["datatype"] = datatype;
77+
6678
# find dimension order
6779
order = cmd("mincinfo -attval image:dimorder {}".format(mincfile))
6880
order = order.split(",")
6981

7082
if len(order) < 3 or len(order) > 4:
7183
order = cmd("mincinfo -dimnames {}".format(mincfile))
7284
order = order.split(" ")
73-
85+
7486
header["order"] = order
7587

76-
if len(order) == 4:
88+
if len(order) == 4:
7789
time_start = cmd("mincinfo -attval time:start {}".format(mincfile))
7890
time_length = cmd("mincinfo -dimlength time {}".format(mincfile))
79-
80-
header["time"] = { "start" : float(time_start),
91+
92+
header["time"] = { "start" : float(time_start),
8193
"space_length" : float(time_length) }
8294

8395
# find space
8496
header["xspace"] = get_space(mincfile,"xspace")
8597
header["yspace"] = get_space(mincfile,"yspace")
86-
header["xspace"] = get_space(mincfile,"xspace")
98+
header["zspace"] = get_space(mincfile,"zspace")
8799

88100
if len(order) > 3:
89101
header["time"] = get_space(mincfile,"time")
90102

91103
# write out the header
92104
open(headerfile,"w").write(json.dumps(header))
93105

94-
def make_raw(mincfile, rawfile):
106+
def make_raw(mincfile, datatype, rawfile):
95107
raw = open(rawfile, "wb")
96-
raw.write(
97-
subprocess.check_output(["minctoraw","-byte","-unsigned","-normalize",mincfile]))
98-
99-
def main(filename):
108+
args = ["-normalize", mincfile]
109+
opts = {
110+
"int8": ["-byte", "-signed"],
111+
"uint8": ["-byte", "-unsigned"],
112+
"int16": ["-short", "-signed"],
113+
"uint16": ["-short", "-unsigned"],
114+
"int32": ["-int", "-signed"],
115+
"uint32": ["-int", "-unsigned"],
116+
"int32": ["-int", "-signed"],
117+
"uint32": ["-int", "-unsigned"],
118+
"float32":["-float"],
119+
"float64":["-double"],
120+
}[datatype]
121+
for opt in opts:
122+
args.insert(0, opt)
123+
args.insert(0, "minctoraw")
124+
raw.write(
125+
subprocess.check_output(args))
126+
127+
def main(filename, datatype):
100128
if not os.path.isfile(filename):
101-
console_error("File {} does not exist.".format(filename))
129+
console_error("File {} does not exist.".format(filename), 1)
102130

103131
check_minc_tools_installed()
104132

@@ -109,14 +137,19 @@ def main(filename):
109137
console_log("Processing file: {}".format(filename))
110138

111139
console_log("Creating header file: {}".format(headername))
112-
make_header(filename, headername)
140+
make_header(filename, datatype, headername)
113141

114142
console_log("Creating raw data file: {}".format(rawname))
115-
make_raw(filename, rawname)
143+
make_raw(filename, datatype, rawname)
116144

117-
if __name__ == '__main__':
145+
if __name__ == '__main__':
118146
parser = argparse.ArgumentParser()
119-
parser.add_argument("filename")
147+
parser.add_argument("filename", help="the minc file to convert")
148+
parser.add_argument("-T", dest="datatype",
149+
choices=["int8", "int16", "int32",
150+
"uint8", "uint16", "uint32",
151+
"float32", "float64"],
152+
action="store", default="uint8",
153+
help="set the voxel data type of the output")
120154
args = parser.parse_args()
121-
122-
main(args.filename)
155+
main(args.filename, args.datatype)

src/brainbrowser/volume-viewer/volume-loaders/mgh.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -341,25 +341,14 @@
341341
break;
342342
}
343343

344-
var d = 0; // Generic loop counter.
345-
var n_min = +Infinity;
346-
var n_max = -Infinity;
347-
348-
for (d = 0; d < native_data.length; d++) {
349-
if (native_data[d] > n_max)
350-
n_max = native_data[d];
351-
if (native_data[d] < n_min)
352-
n_min = native_data[d];
353-
}
354-
355-
header.voxel_min = n_min;
356-
header.voxel_max = n_max;
344+
VolumeViewer.utils.scanDataRange(native_data, header);
357345

358346
// Incrementation offsets for each dimension of the volume. MGH
359347
// files store the fastest-varying dimension _first_, so the
360348
// "first" dimension actually has the smallest offset. That is
361349
// why this calculation is different from that for NIfTI-1.
362350
//
351+
var d;
363352
var offset = 1;
364353
for (d = 0; d < header.order.length; d++) {
365354
header[header.order[d]].offset = offset;

0 commit comments

Comments
 (0)