Skip to content

Commit

Permalink
added naive dcm series support by converting dcm dir into .nii
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakub Belicki committed Apr 25, 2023
1 parent cb04be7 commit 7d66a79
Show file tree
Hide file tree
Showing 7 changed files with 3,419 additions and 4,007 deletions.
7 changes: 5 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/data",
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentPath=${workspaceRoot}",
"--disable-extensions"
]
],
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/dist/**/*.js"]
},
{
"name": "Run Web Extension",
Expand Down
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.indentSize": 2
}
138 changes: 137 additions & 1 deletion extension/provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,138 @@
import * as vscode from 'vscode';
import { Buffer } from 'buffer';
import { NiftiDocument } from '../extension/document';
import { dirname } from 'path';
import daikon from 'daikon';
import { appendFileSync, readFileSync, renameSync } from 'fs';
import { v4 } from 'uuid';
import * as temp from 'temp';
import { assert } from 'console';
temp.track();

function toArrayBuffer(buffer: Uint8Array) {
const arrayBuffer = new ArrayBuffer(buffer.length);
const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return arrayBuffer;
}

async function dcm2nii(uri: vscode.Uri, outUri: vscode.Uri): Promise<vscode.Uri>{
const images = (await vscode.workspace.fs.readDirectory(uri).then(async (dcms) => {
return dcms.map(async (dcm) => {
return daikon.Series.parseImage(new DataView(toArrayBuffer(await vscode.workspace.fs.readFile(vscode.Uri.parse(uri.path + '/' + dcm[0])))));
});
}).then((promises) => Promise.all(promises))).sort((a, b) => {
const a_slic = a.getSliceLocation();
const b_slic = b.getSliceLocation();
return a_slic < b_slic ? -1 : a_slic > b_slic ? 1 : 0;
});
const imgPath = outUri.path + "/" + v4();
const series = new daikon.Series();
let minVal = images[0].getInterpretedData()[0];
let maxVal = images[0].getInterpretedData()[0];
const l = images[0].getInterpretedData().length;
for (const image of images){
if (image === null) {
console.error(daikon.Series.parserError);
} else if (image.hasPixelData()) {
const {data, max, maxIndex, min, minIndex, numCols, numRows} = image.getInterpretedData(true, true);
if (max > maxVal){maxVal = max;}
if (min < minVal){minVal = min;}
assert(l === data.length);
appendFileSync(imgPath, new Uint8Array(new Float32Array(data).buffer));
series.addImage(image);
}
}
series.buildSeries();
const ori = images[0].getTag(0x0020,0x0037).value;
const firstPos = images[0].getTag(0x0020,0x0032).value;
const lastPos = images[images.length - 1].getTag(0x0020,0x0032).value;
const thi = images[0].getTag(0x0018,0x0050).value;
const n = images.length;
// https://brainder.org/2015/04/03/the-nifti-2-file-format/
// https://core.ac.uk/download/pdf/79518053.pdf
const bytes = [
new Uint8Array(new Int32Array([540]).buffer), // sizeof_hdr
Buffer.from("n+2"), // magic[0-2]
new Uint8Array(5),
new Uint8Array(new Int16Array([16]).buffer), // data_type
new Uint8Array(new Int16Array([32]).buffer), // bitpix
new Uint8Array(new BigInt64Array([
BigInt(3), // dim[0]
BigInt(series.images[0].getRows()), // dim[1]
BigInt(series.images[0].getCols()), // dim[2]
BigInt(series.images.length), // dim[3]
BigInt(1), // dim[4]
BigInt(1), // dim[5]
BigInt(1), // dim[6]
BigInt(1), // dim[7]
]).buffer),
new Uint8Array(new Float64Array([
0, // intent_p1
0, // intent_p2
0, // intent_p3
]).buffer),
new Uint8Array(new Float64Array([
0, // pixdim[0]
...series.images[0].getPixelSpacing(), // pixdim[1] pixdim[2]
series.images[0].getSliceThickness(), // pixdim[3]
0, // pixdim[4]
0, // pixdim[5]
0, // pixdim[6]
0, // pixdim[7]
]).buffer),
new Uint8Array(new BigInt64Array([BigInt(544)]).buffer), // vox_offset
new Uint8Array(new Float64Array([
0, //1, // scl_slope
0, //0, // scl_inter
maxVal, // cal_max
minVal, // cal_min
0, // slice_duration
0, // toffset
]).buffer),
new Uint8Array(new BigInt64Array([
BigInt(0), // slice_start
BigInt(0) // slice_end
]).buffer),
new Uint8Array(80), // descrip[80] // some
new Uint8Array(24), // aux_file[24] // none
new Uint8Array(new Int32Array([
0, // qform_code
4, // sform_code
]).buffer),
new Uint8Array(new Float64Array([
0, // quatern_b
0, // quatern_c
0, // quatern_d
...images[0].getImagePosition(), // qoffset_x qoffset_y qoffset_z
// -ori[0]*thi[0], -ori[3]*thi[1], -(lastPos[0] - firstPos[0]) / (n - 1), -firstPos[0], // srow_x[4]
// -ori[1]*thi[0], -ori[4]*thi[1], -(lastPos[1] - firstPos[1]) / (n - 1), -firstPos[1], // srow_y[4]
// ori[2]*thi[0], ori[5]*thi[1], (lastPos[2] - firstPos[2]) / (n - 1), firstPos[2] // srow_z[4]
-1 , 0, 0, 0, // srow_x[4]
0 ,-1, 0, 0, // srow_y[4]
0 , 0, 1, 0 // srow_z[4]
]).buffer),
new Uint8Array(new Int32Array([
0, // slice_code
2, // xyzt_units
0, // intent_code
]).buffer),
new Uint8Array(16), // intent_name[16]
new Uint8Array(1), // dim_info
new Uint8Array(15), // unused_str[15]
new Uint8Array(4), // additional 4 bytes
];
const hdrPath = outUri.path + "/" + v4();
bytes.map((buf) => appendFileSync(hdrPath, buf));
// const outcome = outUri.path + "/" + v4() + ".nii";
console.log(new Uint8Array(readFileSync(imgPath)).length);
appendFileSync(hdrPath, new Uint8Array(readFileSync(imgPath)));
const outcome = hdrPath + ".nii";
renameSync(hdrPath, outcome);
return vscode.Uri.parse(outcome);
}

export class NiftiEditorProvider implements vscode.CustomReadonlyEditorProvider<NiftiDocument> {

Expand Down Expand Up @@ -30,7 +161,12 @@ export class NiftiEditorProvider implements vscode.CustomReadonlyEditorProvider<
uri: vscode.Uri
): Promise<NiftiDocument> {
console.log(`Open document ${uri}`);
const data: Uint8Array = await vscode.workspace.fs.readFile(uri);
let data: Uint8Array = await vscode.workspace.fs.readFile(uri);
if (uri.path.endsWith(".dcm")){
const outDir = temp.mkdirSync(v4());
const uriNii = await dcm2nii(vscode.Uri.parse(dirname(uri.path)), vscode.Uri.parse(outDir));
data = await vscode.workspace.fs.readFile(uriNii);
}
const document: NiftiDocument = new NiftiDocument(uri, data);
return document;
}
Expand Down
Loading

0 comments on commit 7d66a79

Please sign in to comment.