import { Volume, NiftiHeader } from '@/types' import { NVUtilities } from '@/nvutilities' // rotate image to match right-anterior-superior voxel order export function img2ras16(volume: Volume): Int16Array { // return image oriented to RAS space as int16 const dims = volume.hdr.dims // reverse to original const perm = volume.permRAS const vx = dims[1] * dims[2] * dims[3] // this.drawBitmap = new Uint8Array(vx); const img16 = new Int16Array(vx) const layout = [0, 0, 0] for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (Math.abs(perm[i]) - 1 !== j) { continue } layout[j] = i * Math.sign(perm[i]) } } let stride = 1 const instride = [1, 1, 1] const inflip = [false, false, false] for (let i = 0; i < layout.length; i++) { for (let j = 0; j < layout.length; j++) { const a = Math.abs(layout[j]) if (a !== i) { continue } instride[j] = stride // detect -0: https://medium.com/coding-at-dawn/is-negative-zero-0-a-number-in-javascript-c62739f80114 if (layout[j] < 0 || Object.is(layout[j], -0)) { inflip[j] = true } stride *= dims[j + 1] } } let xlut = NVUtilities.range(0, dims[1] - 1, 1) if (inflip[0]) { xlut = NVUtilities.range(dims[1] - 1, 0, -1) } for (let i = 0; i < dims[1]; i++) { xlut[i] *= instride[0] } let ylut = NVUtilities.range(0, dims[2] - 1, 1) if (inflip[1]) { ylut = NVUtilities.range(dims[2] - 1, 0, -1) } for (let i = 0; i < dims[2]; i++) { ylut[i] *= instride[1] } let zlut = NVUtilities.range(0, dims[3] - 1, 1) if (inflip[2]) { zlut = NVUtilities.range(dims[3] - 1, 0, -1) } for (let i = 0; i < dims[3]; i++) { zlut[i] *= instride[2] } // convert data let j = 0 for (let z = 0; z < dims[3]; z++) { for (let y = 0; y < dims[2]; y++) { for (let x = 0; x < dims[1]; x++) { img16[xlut[x] + ylut[y] + zlut[z]] = volume.img[j] j++ } } } return img16 } export function unpackFloatFromVec4i(val: Uint8Array): number { // Convert 32-bit rgba to float32 // https://github.com/rii-mango/Papaya/blob/782a19341af77a510d674c777b6da46afb8c65f1/src/js/viewer/screensurface.js#L552 const bitSh = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0] return (val[0] * bitSh[0] + val[1] * bitSh[1] + val[2] * bitSh[2] + val[3] * bitSh[3]) / 255.0 } /** * Scale the raw intensity values by the header scale slope and intercept * @param hdr - the header object * @param raw - the raw intensity values * @returns the scaled intensity values * @internal */ export function intensityRaw2Scaled(hdr: NiftiHeader, raw: number): number { if (hdr.scl_slope === 0) { hdr.scl_slope = 1.0 } return raw * hdr.scl_slope + hdr.scl_inter }