in src/engine/savers/SaverNifti.js [51:221]
static writeBuffer(volumeData, volumeSize) {
// check input data
const xDim = volumeSize.x;
const yDim = volumeSize.y;
const zDim = volumeSize.z;
if (xDim <= 0 || yDim <= 0 || zDim <= 0) {
console.log(`SaverNifti. volume pixels dim is bad: ${xDim} * ${yDim} * ${zDim} `);
}
const xGrid = volumeSize.pixdim1;
const yGrid = volumeSize.pixdim2;
const zGrid = volumeSize.pixdim3;
const TOO_MUCH = 5.0;
const TOO_MIN = 0.00001;
if (xGrid > TOO_MUCH || yGrid > TOO_MUCH || zGrid > TOO_MUCH) {
console.log(`SaverNifti. volume grid size is too much: ${xGrid} * ${yGrid} * ${zGrid} `);
}
if (xGrid < TOO_MIN || yGrid < TOO_MIN || zGrid < TOO_MIN) {
console.log(`SaverNifti. volume grid size is too min: ${xGrid} * ${yGrid} * ${zGrid} `);
}
if (volumeData.length !== xDim * yDim * zDim) {
console.log(`SaverNifti. bad input volume size = ${volumeData.length}, expected = ${xDim}*${yDim}*${zDim}`);
}
const TOO_BIG_VAL = 1000000;
const TOO_SMALL_VAL = -1000000;
let valMin = TOO_BIG_VAL;
let valMax = TOO_SMALL_VAL;
const numPixels = xDim * yDim * zDim;
for (let i = 0; i < numPixels; i++) {
valMin = volumeData[i] < valMin ? volumeData[i] : valMin;
valMax = volumeData[i] > valMax ? volumeData[i] : valMax;
}
const TOO_MIN_RANGE = 60;
if (valMax - valMin < TOO_MIN_RANGE) {
console.log(`SaverNifti. bad input volume data range: [${valMin} .. ${valMax}]`);
}
const NIFTI_HEADER_SIZE = 348;
const BYTES_PER_ELEMENT = 2;
const arrBuf = new ArrayBuffer(NIFTI_HEADER_SIZE + volumeData.length * BYTES_PER_ELEMENT);
const bufBytes = new Uint8Array(arrBuf);
const SIZE_DWORD = 4;
const SIZE_SHORT = 2;
let bufOff = 0;
SaverNifti.writeIntToBuffer(NIFTI_HEADER_SIZE, arrBuf, bufOff);
bufOff += SIZE_DWORD;
// data type
// eslint-disable-next-line
bufOff += 10;
// db name
// eslint-disable-next-line
bufOff += 18;
// extents
bufOff += SIZE_DWORD;
// session error
bufOff += SIZE_SHORT;
// regular
bufOff += 1;
// dim info
const D_FREQ = 1;
const D_PHASE = 2;
const D_SLICE = 3;
const dimInfo = SaverNifti.fpsToDimInfo(D_FREQ, D_PHASE, D_SLICE);
bufBytes[bufOff] = dimInfo;
bufOff += 1;
// write number of dimensions
const NUM_DIMS = 3;
SaverNifti.writeShortToBuffer(NUM_DIMS, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// dave dims
SaverNifti.writeShortToBuffer(volumeSize.x, arrBuf, bufOff);
bufOff += SIZE_SHORT;
SaverNifti.writeShortToBuffer(volumeSize.y, arrBuf, bufOff);
bufOff += SIZE_SHORT;
SaverNifti.writeShortToBuffer(volumeSize.z, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// eslint-disable-next-line
bufOff += 8;
// intent_p1
bufOff += SIZE_DWORD;
// intent_p2
bufOff += SIZE_DWORD;
// intent_p3
bufOff += SIZE_DWORD;
// intent_code
bufOff += SIZE_SHORT;
// datatype
const DATA_TYPE_UINT16 = 512;
SaverNifti.writeShortToBuffer(DATA_TYPE_UINT16, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// bitpix
const BIT_PIXELS = 16;
SaverNifti.writeShortToBuffer(BIT_PIXELS, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// slice start
bufOff += SIZE_SHORT;
// grid spacing (pixdim)
bufOff += SIZE_DWORD;
SaverNifti.writeFloatToBuffer(volumeSize.pixdim1, arrBuf, bufOff);
bufOff += SIZE_DWORD;
SaverNifti.writeFloatToBuffer(volumeSize.pixdim2, arrBuf, bufOff);
bufOff += SIZE_DWORD;
SaverNifti.writeFloatToBuffer(volumeSize.pixdim3, arrBuf, bufOff);
bufOff += SIZE_DWORD;
// eslint-disable-next-line
bufOff += SIZE_DWORD * 4;
// voxoffset
const VOX_OFFSET = 352.0;
SaverNifti.writeFloatToBuffer(VOX_OFFSET, arrBuf, bufOff);
bufOff += SIZE_DWORD;
// sclSlope
const SLOPE = 1.0;
SaverNifti.writeFloatToBuffer(SLOPE, arrBuf, bufOff);
bufOff += SIZE_DWORD;
// sclInter
const INTER = 0.0;
SaverNifti.writeFloatToBuffer(INTER, arrBuf, bufOff);
bufOff += SIZE_DWORD;
// sliceEnd
bufOff += SIZE_SHORT;
// sliceCode
const SLICE_CODE = 1;
bufBytes[bufOff] = SLICE_CODE;
bufOff++;
// m_xyztUnits
const XYZ_UNITS = 10;
bufBytes[bufOff] = XYZ_UNITS;
bufOff++;
// m_calMax, m_calMin, m_sliceDuration, m_toffset
bufOff += SIZE_DWORD * 4;
// m_glmax, m_glmin
bufOff += SIZE_DWORD * 2;
// m_descrip[80]
bufOff += 80;
// m_auxFile[24]
bufOff += 24;
// m_qformCode
const FORM_CODE = 1;
SaverNifti.writeShortToBuffer(FORM_CODE, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// m_sformCode
SaverNifti.writeShortToBuffer(FORM_CODE, arrBuf, bufOff);
bufOff += SIZE_SHORT;
// 4 last bytes are magic
bufOff = NIFTI_HEADER_SIZE - SIZE_DWORD;
// 'n' == 110, '+' == 43, '1' == 49
const MAG_0 = 110;
const MAG_1 = 43;
const MAG_2 = 49;
bufBytes[bufOff + 0] = MAG_0;
// eslint-disable-next-line
bufBytes[bufOff + 1] = MAG_1;
// eslint-disable-next-line
bufBytes[bufOff + 2] = MAG_2;
bufOff += SIZE_DWORD; // last magic bytes in header
const volDataUInt16 = new Uint16Array(volumeData);
const bufBytes16 = new Uint16Array(arrBuf, bufOff);
bufBytes16.set(volDataUInt16);
return arrBuf;
}