@niivue/dcm2niix
Version:
A javascript library to easily use the WASM build of Chris Rorden's dcm2niix command line program but in the browser.
268 lines (267 loc) • 8.26 kB
JavaScript
// src/index.js
var Dcm2niix = class {
constructor() {
this.worker = null;
}
init() {
this.worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });
return new Promise((resolve, reject) => {
this.worker.onmessage = (event) => {
if (event.data && event.data.type === "ready") {
resolve(true);
}
};
this.worker.onerror = (error) => {
reject(new Error(`Worker failed to load: ${error.message}`));
};
});
}
conformFileList(fileObjectOrArray) {
const filesWithRelativePaths = Array.from(fileObjectOrArray).map((file) => ({
file,
// need to check for both webkitRelativePath and _webkitRelativePath.
// _webkitRelativePath is used in case the file was not from a webkitdirectory file input element (e.g. from a drop event).
// IMPORTANT: it is up to other developers to ensure that the special _webkitRelativePath property is set correctly when using drop events.
webkitRelativePath: file.webkitRelativePath || file._webkitRelativePath || ""
}));
return filesWithRelativePaths;
}
input(fileListObject) {
const conformedFileList = this.conformFileList(fileListObject);
return new Processor({ worker: this.worker, fileList: conformedFileList });
}
inputFromWebkitDirectory(fileListObject) {
const conformedFileList = this.conformFileList(fileListObject);
return new Processor({ worker: this.worker, fileList: conformedFileList });
}
inputFromDropItems(dataTransferItemArray) {
const conformedFileList = this.conformFileList(dataTransferItemArray);
return new Processor({ worker: this.worker, fileList: conformedFileList });
}
};
var Processor = class {
constructor({ worker, fileList }) {
this.worker = worker;
this.fileList = fileList;
this.commands = [];
}
_addCommand(cmd, ...args) {
this.commands.push(cmd, ...args.map(String));
return this;
}
// --version
version() {
return this._addCommand("--version");
}
// -1..-9 gz compression level (1=fastest..9=smallest, default 6)
compressionLevel(level) {
return this._addCommand(`-${level}`);
}
// -a adjacent DICOMs (images from same series always in same folder) for faster conversion (n/y, default n)
a(value) {
return this._addCommand("-a", value);
}
// alias for -a
adjacent(value) {
return this.a(value);
}
// -b BIDS sidecar (y/n/o [o=only: no NIfTI], default y)
b(value) {
return this._addCommand("-b", value);
}
// alias for -b
bids(value) {
return this.b(value);
}
// -ba BIDS anonymize (y/n, default y)
ba(value) {
return this._addCommand("-ba", value);
}
// alias for -ba
bidsAnonymize(value) {
return this.ba(value);
}
// -c comment stored as NIfTI aux_file (up to 24 characters)
c(value) {
return this._addCommand("-c", value);
}
// alias for -c
comment(value) {
return this.c(value);
}
// -d directory search depth (0..9, default 5)
// Note: not used in browser/wasm since file list is a flat list
d(value) {
return this._addCommand("-d", value);
}
// alias for -d
directorySearchDepth(value) {
return this.d(value);
}
// export as NRRD (y) or MGH (o) or JSON/JNIfTI (j) or BJNIfTI (b) instead of NIfTI (y/n/o/j/b, default n)
e(value) {
return this._addCommand("-e", value);
}
// alias for -e
exportFormat(value) {
return this.e(value);
}
// -f : filename (%a=antenna (coil) name, %b=basename, %c=comments, %d=description,
// %e=echo number, %f=folder name, %g=accession number, %i=ID of patient, %j=seriesInstanceUID,
// %k=studyInstanceUID, %m=manufacturer, %n=name of patient, %o=mediaObjectInstanceUID,
// %p=protocol, %r=instance number, %s=series number, %t=time, %u=acquisition number,
// %v=vendor, %x=study ID; %z=sequence name;
//
// default '%f_%p_%t_%s')
f(value) {
return this._addCommand("-f", value);
}
// alias for -f
filenameformat(value) {
return this.f(value);
}
// -i : ignore derived, localizer and 2D images (y/n, default n)
i(value) {
return this._addCommand("-i", value);
}
// alias for -i
ignoreDerived(value) {
return this.i(value);
}
// -l : losslessly scale 16-bit integers to use dynamic range (y/n/o [yes=scale, no=no, but uint16->int16, o=original], default o)
l(value) {
return this._addCommand("-l", value);
}
// alias for -l
losslessScale(value) {
return this.l(value);
}
// -m : merge 2D slices from same series regardless of echo, exposure, etc. (n/y or 0/1/2, default 2) [no, yes, auto]
m(value) {
return this._addCommand("-m", value);
}
// alias for -m
merge2DSlices(value) {
return this.m(value);
}
// -n : only convert this series CRC number - can be used up to 16 times (default convert all)
n(value) {
return this._addCommand("-n", value);
}
// alias for -n
seriesCRC(value) {
return this.n(value);
}
// -o : output directory (omit to save to input folder)
// o(value){
// return this._addCommand('-o', value);
// }
// alias for -o
// outputDirectory(value){
// return this.o(value);
// }
// -p : Philips precise float (not display) scaling (y/n, default y)
p(value) {
return this._addCommand("-p", value);
}
// alias for -p
philipsPreciseFloat(value) {
return this.p(value);
}
// -q : only search directory for DICOMs (y/l/n, default y) [y=show number of DICOMs found, l=additionally list DICOMs found, n=no]
q(value) {
return this._addCommand("-q", value);
}
// alias for -q
searchDirectory(value) {
return this.q(value);
}
// -r : rename instead of convert DICOMs (y/n, default n)
r(value) {
return this._addCommand("-r", value);
}
// alias for -r
renameOnly(value) {
return this.r(value);
}
// -s : single file mode, do not convert other images in folder (y/n, default n)
s(value) {
return this._addCommand("-s", value);
}
// alias for -s
singleFileMode(value) {
return this.s(value);
}
// -v : verbose (n/y or 0/1/2, default 0) [no, yes, logorrheic]
v(value) {
return this._addCommand("-v", value);
}
// alias for -v
verbose(value) {
return this.v(value);
}
// -w : write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix)
w(value) {
return this._addCommand("-w", value);
}
// alias for -w
writeBehavior(value) {
return this.w(value);
}
// -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquisitions)
x(value) {
return this._addCommand("-x", value);
}
// alias for -x
crop(value) {
return this.x(value);
}
// -z : gz compress images (y/o/i/n/3, default n) [y=pigz, o=optimal pigz, i=internal:miniz, n=no, 3=no,3D]
z(value) {
return this._addCommand("-z", value);
}
// alias for -z
gzip(value) {
return this.z(value);
}
// --big-endian : byte order (y/n/o, default o) [y=big-end, n=little-end, o=optimal/native]
bigEndian(value) {
return this._addCommand("--big-endian", value);
}
// --ignore_trigger_times : disregard values in 0018,1060 and 0020,9153
ignoreTriggerTimes() {
return this._addCommand("--ignore_trigger_times");
}
// --terse : omit filename post-fixes (can cause overwrites)
terse() {
return this._addCommand("--terse");
}
// --xml : Slicer format features
xml() {
return this._addCommand("--xml");
}
async run() {
return new Promise((resolve, reject) => {
this.worker.onmessage = (e) => {
if (e.data.type === "error") {
reject(new Error(e.data.message));
} else {
const { convertedFiles, exitCode } = e.data;
if (exitCode === 0 || exitCode === 3) {
resolve(convertedFiles);
} else {
reject(new Error(`dcm2niix processing failed with exit code ${exitCode}`));
}
}
};
const args = [...this.commands];
if (this.worker === null) {
reject(new Error("Worker not initialized. Did you await the init() method?"));
}
this.worker.postMessage({ fileList: this.fileList, cmd: args });
});
}
};
export {
Dcm2niix
};