Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nifti-reader-js

Package Overview
Dependencies
Maintainers
2
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nifti-reader-js - npm Package Compare versions

Comparing version 0.5.4 to 0.6.0

src/nifti-extension.js

3

bower.json

@@ -13,3 +13,4 @@ {

"dependencies": {
"pako": "*"
"pako": "*",
"fflate": "*"
},

@@ -16,0 +17,0 @@ "devDependencies": {

{
"name": "nifti-reader-js",
"version": "0.5.4",
"version": "0.6.0",
"description": "A JavaScript NIfTI file format reader.",

@@ -10,9 +10,9 @@ "main": "src/nifti.js",

"dependencies": {
"pako": "*"
"fflate": "*"
},
"devDependencies": {
"browserify": "*",
"jsdoc-to-markdown": "*",
"mocha": "*",
"browserify": "*",
"uglifyjs": "*",
"jsdoc-to-markdown": "*"
"uglifyjs": "^2.4.11"
},

@@ -19,0 +19,0 @@ "scripts": {

# NIFTI-Reader-JS
A JavaScript [NIfTI](http://nifti.nimh.nih.gov/) file format reader. This reader supports both NIfTI-1 and NIfT1-2 file formats, both compressed (.nii.gz) and uncompressed (.nii).
###Usage
[API](https://github.com/rii-mango/NIFTI-Reader-JS/wiki/API) and [more examples](https://github.com/rii-mango/NIFTI-Reader-JS/tree/master/tests)
### Usage
[API](https://github.com/rii-mango/NIFTI-Reader-JS/wiki/API), [drawing to canvas example](https://github.com/rii-mango/NIFTI-Reader-JS/blob/master/tests/canvas.html) and [more](https://github.com/rii-mango/NIFTI-Reader-JS/tree/master/tests)

@@ -28,3 +28,3 @@ ```javascript

###Install
### Install
Get a packaged source file:

@@ -47,3 +47,3 @@

###Testing
### Testing
```

@@ -53,3 +53,3 @@ npm test

###Building
### Building
See the [release folder](https://github.com/rii-mango/NIFTI-Reader-JS/tree/master/release) for the latest builds or build it yourself using:

@@ -65,3 +65,3 @@ ```

NIFTI-Reader-JS makes use of the following third-party libraries:
- [pako](https://github.com/nodeca/pako) — for GZIP inflating
- [fflate](https://github.com/101arrowz/fflate) — for GZIP inflating

@@ -16,8 +16,7 @@

nifti.NIFTI2 = nifti.NIFTI2 || ((typeof require !== 'undefined') ? require('./nifti2.js') : null);
nifti.NIFTIEXTENSION = nifti.NIFTIEXTENSION || ((typeof require !== 'undefined') ? require('./nifti-extension.js') : null);
nifti.Utils = nifti.Utils || ((typeof require !== 'undefined') ? require('./utilities.js') : null);
var pako = pako || ((typeof require !== 'undefined') ? require('pako') : null);
var fflate = fflate || ((typeof require !== 'undefined') ? require('fflate') : null);
/*** Static Methods ***/

@@ -30,5 +29,4 @@

*/
nifti.isNIFTI1 = function (data) {
nifti.isNIFTI1 = function (data, isHdrImgPairOK = false) {
var buf, mag1, mag2, mag3;
if (data.byteLength < nifti.NIFTI1.STANDARD_HEADER_SIZE) {

@@ -46,2 +44,6 @@ return false;

if ((isHdrImgPairOK) && (mag1 === nifti.NIFTI1.MAGIC_NUMBER2[0]) && (mag2 === nifti.NIFTI1.MAGIC_NUMBER2[1]) &&
(mag3 === nifti.NIFTI1.MAGIC_NUMBER2[2]))
return true; // hdr/img pair
return !!((mag1 === nifti.NIFTI1.MAGIC_NUMBER[0]) && (mag2 === nifti.NIFTI1.MAGIC_NUMBER[1]) &&

@@ -57,3 +59,3 @@ (mag3 === nifti.NIFTI1.MAGIC_NUMBER[2]));

*/
nifti.isNIFTI2 = function (data) {
nifti.isNIFTI2 = function (data, isHdrImgPairOK = false) {
var buf, mag1, mag2, mag3;

@@ -70,2 +72,6 @@

if ((isHdrImgPairOK) && (mag1 === nifti.NIFTI2.MAGIC_NUMBER2[0]) && (mag2 === nifti.NIFTI2.MAGIC_NUMBER2[1]) &&
(mag3 === nifti.NIFTI2.MAGIC_NUMBER2[2]))
return true; // hdr/img pair
return !!((mag1 === nifti.NIFTI2.MAGIC_NUMBER[0]) && (mag2 === nifti.NIFTI2.MAGIC_NUMBER[1]) &&

@@ -82,4 +88,4 @@ (mag3 === nifti.NIFTI2.MAGIC_NUMBER[2]));

*/
nifti.isNIFTI = function (data) {
return (nifti.isNIFTI1(data) || nifti.isNIFTI2(data));
nifti.isNIFTI = function (data, isHdrImgPairOK = false) {
return (nifti.isNIFTI1(data, isHdrImgPairOK) || nifti.isNIFTI2(data, isHdrImgPairOK));
};

@@ -123,3 +129,3 @@

nifti.decompress = function (data) {
return pako.inflate(data).buffer;
return fflate.decompressSync(new Uint8Array(data)).buffer;
};

@@ -134,3 +140,3 @@

*/
nifti.readHeader = function (data) {
nifti.readHeader = function (data, isHdrImgPairOK = false) {
var header = null;

@@ -142,5 +148,5 @@

if (nifti.isNIFTI1(data)) {
if (nifti.isNIFTI1(data, isHdrImgPairOK)) {
header = new nifti.NIFTI1();
} else if (nifti.isNIFTI2(data)) {
} else if (nifti.isNIFTI2(data, isHdrImgPairOK)) {
header = new nifti.NIFTI2();

@@ -221,3 +227,3 @@ }

return data.slice(loc + 8, loc + size - 8);
return data.slice(loc + 8, loc + size); // +8 for loc and -8 for esize and ecode
};

@@ -224,0 +230,0 @@

@@ -7,2 +7,4 @@

const NIFTIEXTENSION = require('./nifti-extension.js');
/*** Imports ***/

@@ -12,5 +14,4 @@

nifti.Utils = nifti.Utils || ((typeof require !== 'undefined') ? require('./utilities.js') : null);
nifti.NIFTIEXTENSION = nifti.NIFTIEXTENSION || ((typeof require !== 'undefined') ? require('./nifti-extension.js') : null);
/*** Constructor ***/

@@ -59,2 +60,3 @@

* @property {number} extensionCode
* @property {nifti.NIFTIEXTENSION[]} extensions
* @type {Function}

@@ -101,2 +103,3 @@ */

this.extensionCode = 0;
this.extensions = [];
};

@@ -227,2 +230,10 @@

this.quatern_d = nifti.Utils.getFloatAt(rawData, 264, this.littleEndian);
// Added by znshje on 27/11/2021
//
// quatern_a is a parameter in quaternion [a, b, c, d], which is required in affine calculation (METHOD 2)
// mentioned in the nifti1.h file
// It can be calculated by a = sqrt(1.0-(b*b+c*c+d*d))
this.quatern_a = Math.sqrt(1.0 -
(Math.pow(this.quatern_b, 2) + Math.pow(this.quatern_c, 2) + Math.pow(this.quatern_d, 2)))
this.qoffset_x = nifti.Utils.getFloatAt(rawData, 268, this.littleEndian);

@@ -232,7 +243,98 @@ this.qoffset_y = nifti.Utils.getFloatAt(rawData, 272, this.littleEndian);

for (ctrOut = 0; ctrOut < 3; ctrOut += 1) {
for (ctrIn = 0; ctrIn < 4; ctrIn += 1) {
index = 280 + (((ctrOut * 4) + ctrIn) * 4);
this.affine[ctrOut][ctrIn] = nifti.Utils.getFloatAt(rawData, index, this.littleEndian);
// Added by znshje on 27/11/2021
//
/* See: https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h */
if (this.qform_code > 0) {
// METHOD 2 (used when qform_code > 0, which should be the "normal" case):
// ---------------------------------------------------------------------
// The (x,y,z) coordinates are given by the pixdim[] scales, a rotation
// matrix, and a shift. This method is intended to represent
// "scanner-anatomical" coordinates, which are often embedded in the
// image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030),
// and (0018,0050)), and represent the nominal orientation and location of
// the data. This method can also be used to represent "aligned"
// coordinates, which would typically result from some post-acquisition
// alignment of the volume to a standard orientation (e.g., the same
// subject on another day, or a rigid rotation to true anatomical
// orientation from the tilted position of the subject in the scanner).
// The formula for (x,y,z) in terms of header parameters and (i,j,k) is:
//
// [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ]
// [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ]
// [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ]
//
// The qoffset_* shifts are in the NIFTI-1 header. Note that the center
// of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is
// just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z).
//
// The rotation matrix R is calculated from the quatern_* parameters.
// This calculation is described below.
//
// The scaling factor qfac is either 1 or -1. The rotation matrix R
// defined by the quaternion parameters is "proper" (has determinant 1).
// This may not fit the needs of the data; for example, if the image
// grid is
// i increases from Left-to-Right
// j increases from Anterior-to-Posterior
// k increases from Inferior-to-Superior
// Then (i,j,k) is a left-handed triple. In this example, if qfac=1,
// the R matrix would have to be
//
// [ 1 0 0 ]
// [ 0 -1 0 ] which is "improper" (determinant = -1).
// [ 0 0 1 ]
//
// If we set qfac=-1, then the R matrix would be
//
// [ 1 0 0 ]
// [ 0 -1 0 ] which is proper.
// [ 0 0 -1 ]
//
// This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0]
// (which encodes a 180 degree rotation about the x-axis).
// Define a, b, c, d for coding covenience
const a = this.quatern_a
const b = this.quatern_b
const c = this.quatern_c
const d = this.quatern_d
this.qfac = this.pixDims[0] === 0 ? 1 : this.pixDims[0]
this.quatern_R = [
[a * a + b * b - c * c - d * d, 2 * b * c - 2 * a * d, 2 * b * d + 2 * a * c],
[2 * b * c + 2 * a * d, a * a + c * c - b * b - d * d, 2 * c * d - 2 * a * b],
[2 * b * d - 2 * a * c, 2 * c * d + 2 * a * b, a * a + d * d - c * c - b * b]
]
for (ctrOut = 0; ctrOut < 3; ctrOut += 1) {
for (ctrIn = 0; ctrIn < 3; ctrIn += 1) {
this.affine[ctrOut][ctrIn] = this.quatern_R[ctrOut][ctrIn] * this.pixDims[ctrIn + 1];
if (ctrIn === 2) {
this.affine[ctrOut][ctrIn] *= this.qfac;
}
}
}
// The last row of affine matrix is the offset vector
this.affine[0][3] = this.qoffset_x
this.affine[1][3] = this.qoffset_y
this.affine[2][3] = this.qoffset_z
} else if (this.sform_code > 0) {
// METHOD 3 (used when sform_code > 0):
// -----------------------------------
// The (x,y,z) coordinates are given by a general affine transformation
// of the (i,j,k) indexes:
//
// x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3]
// y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3]
// z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3]
//
// The srow_* vectors are in the NIFTI_1 header. Note that no use is
// made of pixdim[] in this method.
for (ctrOut = 0; ctrOut < 3; ctrOut += 1) {
for (ctrIn = 0; ctrIn < 4; ctrIn += 1) {
index = 280 + (((ctrOut * 4) + ctrIn) * 4);
this.affine[ctrOut][ctrIn] = nifti.Utils.getFloatAt(rawData, index, this.littleEndian);
}
}
}

@@ -255,6 +357,13 @@

this.extensionFlag[3] = nifti.Utils.getByteAt(rawData, 348 + 3);
if (this.extensionFlag[0]) {
// read our extensions
this.extensions = nifti.Utils.getExtensionsAt(rawData,
this.getExtensionLocation(),
this.littleEndian,
this.vox_offset);
if (this.extensionFlag[0]) {
this.extensionSize = this.getExtensionSize(rawData);
this.extensionCode = this.getExtensionCode(rawData);
// set the extensionSize and extensionCode from the first extension found
this.extensionSize = this.extensions[0].esize;
this.extensionCode = this.extensions[0].ecode;
}

@@ -855,4 +964,2 @@ }

/**

@@ -867,4 +974,139 @@ * Returns the extension code.

/**
* Adds an extension
* @param {NIFTIEXTENSION} extension
* @param {number} index
*/
nifti.NIFTI1.prototype.addExtension = function(extension, index = -1) {
if(index == -1) {
this.extensions.push(extension);
}
else {
this.extensions.splice(index, 0, extension);
}
this.vox_offset += extension.esize;
}
/**
* Removes an extension
* @param {number} index
*/
nifti.NIFTI1.prototype.removeExtension = function(index) {
let extension = this.extensions[index];
if(extension) {
this.vox_offset -= extension.esize;
}
this.extensions.splice(index, 1);
}
/**
* Returns header as ArrayBuffer.
* @param {boolean} includeExtensions - should extension bytes be included
* @returns {ArrayBuffer}
*/
nifti.NIFTI1.prototype.toArrayBuffer = function(includeExtensions = false) {
const SHORT_SIZE = 2;
const FLOAT32_SIZE = 4;
let byteSize = 348 + 4; // + 4 for the extension bytes
// calculate necessary size
if(includeExtensions) {
for(let extension of this.extensions) {
byteSize += extension.esize;
}
}
let byteArray = new Uint8Array(byteSize);
let view = new DataView(byteArray.buffer);
// sizeof_hdr
view.setInt32(0, 348, this.littleEndian);
// data_type, db_name, extents, session_error, regular are not used
// dim_info
view.setUint8(39, this.dim_info);
// dims
for(let i = 0; i < 8; i++) {
view.setUint16(40 + SHORT_SIZE * i, this.dims[i], this.littleEndian);
}
// intent_p1, intent_p2, intent_p3
view.setFloat32(56, this.intent_p1, this.littleEndian);
view.setFloat32(60, this.intent_p2, this.littleEndian);
view.setFloat32(64, this.intent_p3, this.littleEndian);
// intent_code, datatype, bitpix, slice_start
view.setInt16(68, this.intent_code, this.littleEndian);
view.setInt16(70, this.datatypeCode, this.littleEndian);
view.setInt16(72, this.numBitsPerVoxel, this.littleEndian);
view.setInt16(74, this.slice_start, this.littleEndian);
// pixdim[8], vox_offset, scl_slope, scl_inter
for(let i = 0; i < 8; i++) {
view.setFloat32(76 + FLOAT32_SIZE * i, this.pixDims[i], this.littleEndian);
}
view.setFloat32(108, this.vox_offset, this.littleEndian);
view.setFloat32(112, this.scl_slope, this.littleEndian);
view.setFloat32(116, this.scl_inter, this.littleEndian);
// slice_end
view.setInt16(120, this.slice_end, this.littleEndian);
// slice_code, xyzt_units
view.setUint8(122, this.slice_code);
view.setUint8(123, this.xyzt_units);
// cal_max, cal_min, slice_duration, toffset
view.setFloat32(124, this.cal_max, this.littleEndian);
view.setFloat32(128, this.cal_min, this.littleEndian);
view.setFloat32(132, this.slice_duration, this.littleEndian);
view.setFloat32(136, this.toffset, this.littleEndian);
// glmax, glmin are unused
// descrip and aux_file
byteArray.set(Buffer.from(this.description), 148);
byteArray.set(Buffer.from(this.aux_file), 228);
// qform_code, sform_code
view.setInt16(252, this.qform_code, this.littleEndian);
view.setInt16(254, this.sform_code, this.littleEndian);
// quatern_b, quatern_c, quatern_d, qoffset_x, qoffset_y, qoffset_z, srow_x[4], srow_y[4], and srow_z[4]
view.setFloat32(256, this.quatern_b, this.littleEndian);
view.setFloat32(260, this.quatern_c, this.littleEndian);
view.setFloat32(264, this.quatern_d, this.littleEndian);
view.setFloat32(268, this.qoffset_x, this.littleEndian);
view.setFloat32(272, this.qoffset_y, this.littleEndian);
view.setFloat32(276, this.qoffset_z, this.littleEndian);
const flattened = this.affine.flat();
// we only want the first three rows
for(let i = 0; i < 12; i++) {
view.setFloat32(280 + FLOAT32_SIZE * i, flattened[i], this.littleEndian);
}
// intent_name and magic
byteArray.set(Buffer.from(this.intent_name), 328);
byteArray.set(Buffer.from(this.magic), 344);
// add our extension data
if(includeExtensions) {
byteArray.set(Uint8Array.from([1, 0, 0, 0]), 348);
let extensionByteIndex = this.getExtensionLocation();
for(const extension of this.extensions) {
view.setInt32(extensionByteIndex, extension.esize, extension.littleEndian);
view.setInt32(extensionByteIndex + 4, extension.ecode, extension.littleEndian);
byteArray.set(new Uint8Array(extension.edata), extensionByteIndex + 8);
extensionByteIndex += extension.esize;
}
}
else {
// In a .nii file, these 4 bytes will always be present
byteArray.set(new Uint8Array(4).fill(0), 348);
}
return byteArray.buffer;
};
/*** Exports ***/

@@ -871,0 +1113,0 @@

@@ -12,4 +12,4 @@

nifti.NIFTI1 = nifti.NIFTI1 || ((typeof require !== 'undefined') ? require('./nifti1.js') : null);
nifti.NIFTIEXTENSION = nifti.NIFTIEXTENSION || ((typeof require !== 'undefined') ? require('./nifti-extension.js') : null);
/*** Constructor ***/

@@ -55,2 +55,3 @@

* @property {number[]} extensionFlag
* @property {nifti.NIFTIEXTENSION[]} extensions
* @type {Function}

@@ -94,2 +95,3 @@ */

this.extensionFlag = [0, 0, 0, 0];
this.extensions = [];
};

@@ -104,2 +106,3 @@

nifti.NIFTI2.MAGIC_NUMBER = [0x6E, 0x2B, 0x32, 0, 0x0D, 0x0A, 0x1A, 0x0A]; // n+2\0
nifti.NIFTI2.MAGIC_NUMBER2 = [0x6E, 0x69, 0x32, 0, 0x0D, 0x0A, 0x1A, 0x0A]; // ni2\0

@@ -131,3 +134,3 @@

}
this.magic = nifti.Utils.getStringAt(rawData, 4, 12);
this.datatypeCode = nifti.Utils.getShortAt(rawData, 12, this.littleEndian);

@@ -204,4 +207,11 @@ this.numBitsPerVoxel = nifti.Utils.getShortAt(rawData, 14, this.littleEndian);

if (this.extensionFlag[0]) {
this.extensionSize = this.getExtensionSize(rawData);
this.extensionCode = this.getExtensionCode(rawData);
// read our extensions
this.extensions = nifti.Utils.getExtensionsAt(rawData,
this.getExtensionLocation(),
this.littleEndian,
this.vox_offset);
// set the extensionSize and extensionCode from the first extension found
this.extensionSize = this.extensions[0].esize;
this.extensionCode = this.extensions[0].ecode;
}

@@ -316,4 +326,2 @@ }

/**

@@ -326,4 +334,17 @@ * Returns the extension code.

/**
* Adds an extension
* @param {NIFTIEXTENSION} extension
* @param {number} index
*/
nifti.NIFTI2.prototype.addExtension = nifti.NIFTI1.prototype.addExtension;
/**
* Removes an extension
* @param {number} index
*/
nifti.NIFTI2.prototype.removeExtension = nifti.NIFTI1.prototype.removeExtension;
/**

@@ -397,4 +418,146 @@ * Returns a human-readable string of datatype.

/**
* Returns header as ArrayBuffer.
* @param {boolean} includeExtensions - should extension bytes be included
* @returns {ArrayBuffer}
*/
nifti.NIFTI2.prototype.toArrayBuffer = function(includeExtensions = false) {
const INT64_SIZE = 8;
const DOUBLE_SIZE = 8;
let byteSize = 540 + 4; // +4 for extension bytes
// calculate necessary size
if(includeExtensions) {
for(let extension of this.extensions) {
byteSize += extension.esize;
}
}
let byteArray = new Uint8Array(byteSize);
let view = new DataView(byteArray.buffer);
// sizeof_hdr
view.setInt32(0, 540, this.littleEndian);
// magic
byteArray.set(Buffer.from(this.magic), 4);
// datatype
view.setInt16(12, this.datatypeCode, this.littleEndian);
// bitpix
view.setInt16(14, this.numBitsPerVoxel, this.littleEndian);
// dim[8]
for(let i = 0; i < 8; i++) {
view.setBigInt64(16 + INT64_SIZE * i, BigInt(this.dims[i]), this.littleEndian);
}
// intent_p1
view.setFloat64(80, this.intent_p1, this.littleEndian);
// intent_p2
view.setFloat64(88, this.intent_p2, this.littleEndian);
// intent_p3
view.setFloat64(96, this.intent_p3, this.littleEndian);
// pixdim
for(let i = 0; i < 8; i++) {
view.setFloat64(104 + DOUBLE_SIZE * i, this.pixDims[i], this.littleEndian);
}
// vox_offset
view.setBigInt64(168, BigInt(this.vox_offset), this.littleEndian);
// scl_slope
view.setFloat64(176, this.scl_slope, this.littleEndian);
// scl_inter
view.setFloat64(184, this.scl_inter, this.littleEndian);
// cal_max
view.setFloat64(192, this.cal_max, this.littleEndian);
// cal_min
view.setFloat64(200, this.cal_min, this.littleEndian);
// slice_duration
view.setFloat64(208, this.slice_duration, this.littleEndian);
// toffset
view.setFloat64(216, this.toffset, this.littleEndian);
// slice_start
view.setBigInt64(224, BigInt(this.slice_start), this.littleEndian);
// slice end
view.setBigInt64(232, BigInt(this.slice_end), this.littleEndian);
// descrip
byteArray.set(Buffer.from(this.description), 240);
// aux_file
byteArray.set(Buffer.from(this.aux_file), 320);
// qform_code
view.setInt32(344, this.qform_code, this.littleEndian);
// sform_code
view.setInt32(348, this.sform_code, this.littleEndian);
// quatern_b
view.setFloat64(352, this.quatern_b, this.littleEndian);
// quatern_c
view.setFloat64(360, this.quatern_c, this.littleEndian);
// quatern_d
view.setFloat64(368, this.quatern_d, this.littleEndian);
// qoffset_x
view.setFloat64(376, this.qoffset_x, this.littleEndian);
// qoffset_y
view.setFloat64(384, this.qoffset_y, this.littleEndian);
// qoffset_z
view.setFloat64(392, this.qoffset_z, this.littleEndian);
// srow_x[4], srow_y[4], and srow_z[4]
const flattened = this.affine.flat();
// we only want the first three rows
for(let i = 0; i < 12; i++) {
view.setFloat64(400 + DOUBLE_SIZE * i, flattened[i], this.littleEndian);
}
// slice_code
view.setInt32(496, this.slice_code, this.littleEndian);
// xyzt_units
view.setInt32(500, this.xyzt_units, this.littleEndian);
// intent_code
view.setInt32(504, this.intent_code, this.littleEndian);
// intent_name
byteArray.set(Buffer.from(this.intent_name), 508);
// dim_info
view.setUint8(524, this.dim_info);
// add our extension data
if(includeExtensions) {
byteArray.set(Uint8Array.from([1, 0, 0, 0]), 540);
let extensionByteIndex = this.getExtensionLocation();
for(const extension of this.extensions) {
view.setInt32(extensionByteIndex, extension.esize, extension.littleEndian);
view.setInt32(extensionByteIndex + 4, extension.ecode, extension.littleEndian);
byteArray.set(new Uint8Array(extension.edata), extensionByteIndex + 8);
extensionByteIndex += extension.esize;
}
}
else {
// In a .nii file, these 4 bytes will always be present
byteArray.set(new Uint8Array(4).fill(0), 540);
}
return byteArray.buffer;
}
/*** Exports ***/

@@ -401,0 +564,0 @@

@@ -11,5 +11,5 @@

nifti.Utils = nifti.Utils || {};
nifti.NIFTIEXTENSION = nifti.NIFTIEXTENSION || ((typeof require !== 'undefined') ? require('./nifti-extension.js') : null);
/*** Static Pseudo-constants ***/

@@ -85,4 +85,42 @@

nifti.Utils.getExtensionsAt = function (data, start, littleEndian, voxOffset) {
let extensions = [];
let extensionByteIndex = start;
// Multiple extended header sections are allowed
while(extensionByteIndex < voxOffset ) {
// assume same endianess as header until proven otherwise
let extensionLittleEndian = littleEndian;
let esize = nifti.Utils.getIntAt(data, extensionByteIndex, littleEndian);
if(!esize) {
break; // no more extensions
}
// check if this takes us past vox_offset
if(esize + extensionByteIndex > voxOffset) {
// check if reversing byte order gets a proper size
extensionLittleEndian = !extensionLittleEndian;
esize = nifti.Utils.getIntAt(data, extensionByteIndex, extensionLittleEndian);
if(esize + extensionByteIndex > voxOffset) {
throw new Error('This does not appear to be a valid NIFTI extension');
}
}
// esize must be a positive integral multiple of 16
if(esize % 16 != 0) {
throw new Error("This does not appear to be a NIFTI extension");
}
let ecode = nifti.Utils.getIntAt(data, extensionByteIndex + 4, extensionLittleEndian);
let edata = data.buffer.slice(extensionByteIndex + 8, extensionByteIndex + esize);
console.log('extensionByteIndex: ' + (extensionByteIndex + 8) + ' esize: ' + esize);
console.log(edata);
let extension = new nifti.NIFTIEXTENSION(esize, ecode, edata, extensionLittleEndian);
extensions.push(extension);
extensionByteIndex += esize;
}
return extensions;
}
nifti.Utils.toArrayBuffer = function (buffer) {

@@ -89,0 +127,0 @@ var ab, view, i;

@@ -12,3 +12,3 @@

var buf = fs.readFileSync('./tests/data/5D.nii');
var buf = fs.readFileSync('./tests/data/5D_zeros.nii.gz');
var data = nifti.Utils.toArrayBuffer(buf);

@@ -19,3 +19,14 @@ var nifti1 = null;

describe('NIFTI-Reader-JS', function () {
describe('uncompressed nifti-1 test', function () {
describe('compressed 5D nifti-1 test', function () {
it('isCompressed() should return true', function () {
assert.equal(true, nifti.isCompressed(data));
});
it('should not throw error when decompressing', function (done) {
assert.doesNotThrow(function() {
data = nifti.decompress(data);
done();
});
});
it('should not throw error when reading header', function (done) {

@@ -52,3 +63,9 @@ assert.doesNotThrow(function() {

});
it('image data checksum should equal 1033497386', function () {
var imageData = nifti.readImage(nifti1, data);
var checksum = nifti.Utils.crc32(new DataView(imageData));
assert.equal(checksum, 2980574675);
});
});
});

@@ -12,5 +12,8 @@

// var buf = fs.readFileSync('./tests/data/afni.nii.gz');
var buf = fs.readFileSync('./tests/data/with_extension.nii.gz');
var data = nifti.Utils.toArrayBuffer(buf);
var nifti1 = null;
var extension = null;
const EXPECTED_EXTENSION_LENGTH = 376;

@@ -41,6 +44,86 @@ describe('NIFTI-Reader-JS', function () {

it('extension length should be 368', function () {
assert.equal(368, nifti.readExtensionData(nifti1, data).byteLength);
it('extension length should be 376 (384 - 8)', function () {
assert.equal(EXPECTED_EXTENSION_LENGTH + 8, nifti1.getExtensionSize(new DataView(data)));
assert.equal(EXPECTED_EXTENSION_LENGTH, nifti.readExtensionData(nifti1, data).byteLength);
});
it('should have one extension that is 376 bytes', function() {
extension = nifti1.extensions[0];
assert.equal(EXPECTED_EXTENSION_LENGTH, extension.edata.byteLength);
assert.equal(1, nifti1.extensions.length);
});
it('removed extension changes the vox offset', function() {
extension = nifti1.extensions[0];
assert.equal(EXPECTED_EXTENSION_LENGTH, extension.edata.byteLength);
assert.equal(1, nifti1.extensions.length);
});
it('removed extension updates the vox offset', function() {
let oldVoxOffset = nifti1.vox_offset;
nifti1.removeExtension(0);
assert.equal(0, nifti1.extensions.length);
assert.equal(nifti1.vox_offset + extension.esize, oldVoxOffset);
});
it('added extension updates vox_offset', function() {
let oldVoxOffset = nifti1.vox_offset;
nifti1.addExtension(extension);
assert.equal(1, nifti1.extensions.length);
assert.equal(nifti1.vox_offset, oldVoxOffset + extension.esize);
});
it('toArrayBuffer properly allocates extension byte array', function() {
assert.equal(1, nifti1.extensions.length);
let bytesWithHeader = nifti1.toArrayBuffer(true);
let bytesWithoutHeader = nifti1.toArrayBuffer();
let headerBytesGreater = bytesWithHeader.byteLength > bytesWithoutHeader.byteLength;
assert.equal(true, headerBytesGreater);
});
it('toArrayBuffer properly preserves extension bytes', function() {
let bytes = nifti1.toArrayBuffer(true);
let copy = nifti.readHeader(bytes);
assert.equal(1, copy.extensions.length);
assert.equal(EXPECTED_EXTENSION_LENGTH, copy.extensions[0].edata.byteLength);
});
it('extensions can be added and serialized', function() {
let edata = new Int32Array(6);
edata.fill(8);
let newExtension = new nifti.NIFTIEXTENSION(32, 4, edata.buffer, true);
nifti1.addExtension(newExtension);
assert.equal(2, nifti1.extensions.length);
let bytes = nifti1.toArrayBuffer(true);
let copy = nifti.readHeader(bytes);
assert.equal(2, copy.extensions.length);
assert.equal(4, copy.extensions[1].ecode);
assert.equal(24, copy.extensions[1].edata.byteLength);
});
it('extensions can be removed by index', function() {
nifti1.removeExtension(1);
assert.equal(1, nifti1.extensions.length);
let bytes = nifti1.toArrayBuffer(true);
let copy = nifti.readHeader(bytes);
assert.equal(1, copy.extensions.length);
assert.equal(EXPECTED_EXTENSION_LENGTH, copy.extensions[0].edata.byteLength);
})
it('extensions can be inserted and serialized', function() {
let newExtension = new nifti.NIFTIEXTENSION(32, 4, new Uint8Array(16), true);
nifti1.addExtension(newExtension, 0);
assert.equal(2, nifti1.extensions.length);
let bytes = nifti1.toArrayBuffer(true);
let copy = nifti.readHeader(bytes);
assert.equal(2, copy.extensions.length);
assert.equal(4, copy.extensions[0].ecode);
assert.equal(32, copy.extensions[0].esize);
assert.equal(24, copy.extensions[0].edata.byteLength);
})
});
});

@@ -15,2 +15,4 @@

var nifti1 = null;
var bytes = null;
var clone = null;

@@ -55,3 +57,11 @@ describe('NIFTI-Reader-JS', function () {

});
it('data returned from toArrayBuffer preserves all nifti-1 properties', function() {
nifti1 = nifti.readHeader(data);
bytes = nifti1.toArrayBuffer();
clone = nifti.readHeader(bytes);
assert.deepEqual(clone, nifti1);
});
});
});

@@ -15,2 +15,4 @@

var nifti2 = null;
var bytes = null;
var clone = null;

@@ -62,3 +64,10 @@ describe('NIFTI-Reader-JS', function () {

});
it('data returned from toArrayBuffer preserves all nifti-2 properties', function() {
bytes = nifti2.toArrayBuffer();
clone = nifti.readHeader(bytes);
assert.deepEqual(clone, nifti2);
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc