itk-wasm
Version:
High-performance spatial analysis in a web browser, Node.js, and reproducible execution across programming languages and hardware architectures.
422 lines • 23.9 kB
JavaScript
import InterfaceTypes from '../../interface-types/interface-types.js';
import bufferToTypedArray from '../../buffer-to-typed-array.js';
import FloatTypes from '../../interface-types/float-types.js';
import IntTypes from '../../interface-types/int-types.js';
const haveSharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';
const encoder = new TextEncoder();
const decoder = new TextDecoder('utf-8');
function readFileSharedArray(emscriptenModule, path) {
const opts = { flags: 'r', encoding: 'binary' };
const stream = emscriptenModule.fs_open(path, opts.flags);
const stat = emscriptenModule.fs_stat(path);
const length = stat.size;
let arrayBufferData = null;
if (haveSharedArrayBuffer) {
arrayBufferData = new SharedArrayBuffer(length); // eslint-disable-line
}
else {
arrayBufferData = new ArrayBuffer(length);
}
const array = new Uint8Array(arrayBufferData);
emscriptenModule.fs_read(stream, array, 0, length, 0);
emscriptenModule.fs_close(stream);
return array;
}
function memoryUint8SharedArray(emscriptenModule, byteOffset, length) {
let arrayBufferData = null;
if (haveSharedArrayBuffer) {
arrayBufferData = new SharedArrayBuffer(length); // eslint-disable-line
}
else {
arrayBufferData = new ArrayBuffer(length);
}
const array = new Uint8Array(arrayBufferData);
const dataArrayView = new Uint8Array(emscriptenModule.HEAPU8.buffer, byteOffset, length);
array.set(dataArrayView);
return array;
}
function setPipelineModuleInputArray(emscriptenModule, dataArray, inputIndex, subIndex) {
let dataPtr = 0;
if (dataArray !== null) {
dataPtr = emscriptenModule.ccall('itk_wasm_input_array_alloc', 'number', ['number', 'number', 'number', 'number'], [0, inputIndex, subIndex, dataArray.buffer.byteLength]);
emscriptenModule.HEAPU8.set(new Uint8Array(dataArray.buffer), dataPtr);
}
return dataPtr;
}
function setPipelineModuleInputJSON(emscriptenModule, dataObject, inputIndex) {
const dataJSON = JSON.stringify(dataObject);
const length = emscriptenModule.lengthBytesUTF8(dataJSON) + 1;
const jsonPtr = emscriptenModule.ccall('itk_wasm_input_json_alloc', 'number', ['number', 'number', 'number'], [0, inputIndex, length]);
emscriptenModule.stringToUTF8(dataJSON, jsonPtr, length);
}
function getPipelineModuleOutputArray(emscriptenModule, outputIndex, subIndex, componentType) {
const dataPtr = emscriptenModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, outputIndex, subIndex]);
const dataSize = emscriptenModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, outputIndex, subIndex]);
const dataUint8 = memoryUint8SharedArray(emscriptenModule, dataPtr, dataSize);
const data = bufferToTypedArray(componentType, dataUint8.buffer);
return data;
}
function getPipelineModuleOutputJSON(emscriptenModule, outputIndex) {
const jsonPtr = emscriptenModule.ccall('itk_wasm_output_json_address', 'number', ['number', 'number'], [0, outputIndex]);
const dataJSON = emscriptenModule.UTF8ToString(jsonPtr);
const dataObject = JSON.parse(dataJSON);
return dataObject;
}
function runPipelineEmscripten(pipelineModule, args, outputs, inputs) {
if (!(inputs == null) && inputs.length > 0) {
inputs.forEach(function (input, index) {
switch (input.type) {
case InterfaceTypes.TextStream: {
const dataArray = encoder.encode(input.data.data);
const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
const dataJSON = {
size: dataArray.buffer.byteLength,
data: `data:application/vnd.itk.address,0:${arrayPtr}`
};
setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
break;
}
case InterfaceTypes.JsonCompatible: {
const dataArray = encoder.encode(JSON.stringify(input.data));
const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
const dataJSON = {
size: dataArray.buffer.byteLength,
data: `data:application/vnd.itk.address,0:${arrayPtr}`
};
setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
break;
}
case InterfaceTypes.BinaryStream: {
const dataArray = input.data.data;
const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
const dataJSON = {
size: dataArray.buffer.byteLength,
data: `data:application/vnd.itk.address,0:${arrayPtr}`
};
setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
break;
}
case InterfaceTypes.TextFile: {
pipelineModule.fs_writeFile(input.data.path, input.data.data);
break;
}
case InterfaceTypes.BinaryFile: {
pipelineModule.fs_writeFile(input.data.path, input.data.data);
break;
}
case InterfaceTypes.Image: {
const image = input.data;
const dataPtr = setPipelineModuleInputArray(pipelineModule, image.data, index, 0);
const directionPtr = setPipelineModuleInputArray(pipelineModule, image.direction, index, 1);
const metadata = typeof image.metadata?.entries !== 'undefined'
? Array.from(image.metadata.entries())
: [];
const imageJSON = {
imageType: image.imageType,
name: image.name,
origin: image.origin,
spacing: image.spacing,
direction: `data:application/vnd.itk.address,0:${directionPtr}`,
size: image.size,
data: `data:application/vnd.itk.address,0:${dataPtr}`,
metadata
};
setPipelineModuleInputJSON(pipelineModule, imageJSON, index);
break;
}
case InterfaceTypes.Mesh: {
const mesh = input.data;
const pointsPtr = setPipelineModuleInputArray(pipelineModule, mesh.points, index, 0);
const cellsPtr = setPipelineModuleInputArray(pipelineModule, mesh.cells, index, 1);
const pointDataPtr = setPipelineModuleInputArray(pipelineModule, mesh.pointData, index, 2);
const cellDataPtr = setPipelineModuleInputArray(pipelineModule, mesh.cellData, index, 3);
const meshJSON = {
meshType: mesh.meshType,
name: mesh.name,
numberOfPoints: mesh.numberOfPoints,
points: `data:application/vnd.itk.address,0:${pointsPtr}`,
numberOfCells: mesh.numberOfCells,
cells: `data:application/vnd.itk.address,0:${cellsPtr}`,
cellBufferSize: mesh.cellBufferSize,
numberOfPointPixels: mesh.numberOfPointPixels,
pointData: `data:application/vnd.itk.address,0:${pointDataPtr}`,
numberOfCellPixels: mesh.numberOfCellPixels,
cellData: `data:application/vnd.itk.address,0:${cellDataPtr}`
};
setPipelineModuleInputJSON(pipelineModule, meshJSON, index);
break;
}
case InterfaceTypes.PointSet: {
const pointSet = input.data;
const pointsPtr = setPipelineModuleInputArray(pipelineModule, pointSet.points, index, 0);
const pointDataPtr = setPipelineModuleInputArray(pipelineModule, pointSet.pointData, index, 1);
const pointSetJSON = {
pointSetType: pointSet.pointSetType,
name: pointSet.name,
numberOfPoints: pointSet.numberOfPoints,
points: `data:application/vnd.itk.address,0:${pointsPtr}`,
numberOfPointPixels: pointSet.numberOfPointPixels,
pointData: `data:application/vnd.itk.address,0:${pointDataPtr}`
};
setPipelineModuleInputJSON(pipelineModule, pointSetJSON, index);
break;
}
case InterfaceTypes.TransformList: {
const transformList = input.data;
const transformListJSON = [];
transformList.forEach((transform, transformIndex) => {
const fixedParameterPtr = setPipelineModuleInputArray(pipelineModule, transform.fixedParameters, index, transformIndex * 2);
const fixedParameters = `data:application/vnd.itk.address,0:${fixedParameterPtr}`;
const parameterPtr = setPipelineModuleInputArray(pipelineModule, transform.parameters, index, transformIndex * 2 + 1);
const parameters = `data:application/vnd.itk.address,0:${parameterPtr}`;
const transformJSON = {
transformType: transform.transformType,
numberOfFixedParameters: transform.numberOfFixedParameters,
numberOfParameters: transform.numberOfParameters,
name: transform.name,
inputSpaceName: transform.inputSpaceName,
outputSpaceName: transform.outputSpaceName,
parameters,
fixedParameters
};
transformListJSON.push(transformJSON);
});
setPipelineModuleInputJSON(pipelineModule, transformListJSON, index);
break;
}
case InterfaceTypes.PolyData: {
const polyData = input.data;
const pointsPtr = setPipelineModuleInputArray(pipelineModule, polyData.points, index, 0);
const verticesPtr = setPipelineModuleInputArray(pipelineModule, polyData.vertices, index, 1);
const linesPtr = setPipelineModuleInputArray(pipelineModule, polyData.lines, index, 2);
const polygonsPtr = setPipelineModuleInputArray(pipelineModule, polyData.polygons, index, 3);
const triangleStripsPtr = setPipelineModuleInputArray(pipelineModule, polyData.triangleStrips, index, 4);
const pointDataPtr = setPipelineModuleInputArray(pipelineModule, polyData.pointData, index, 5);
const cellDataPtr = setPipelineModuleInputArray(pipelineModule, polyData.pointData, index, 6);
const polyDataJSON = {
polyDataType: polyData.polyDataType,
name: polyData.name,
numberOfPoints: polyData.numberOfPoints,
points: `data:application/vnd.itk.address,0:${pointsPtr}`,
verticesBufferSize: polyData.verticesBufferSize,
vertices: `data:application/vnd.itk.address,0:${verticesPtr}`,
linesBufferSize: polyData.linesBufferSize,
lines: `data:application/vnd.itk.address,0:${linesPtr}`,
polygonsBufferSize: polyData.polygonsBufferSize,
polygons: `data:application/vnd.itk.address,0:${polygonsPtr}`,
triangleStripsBufferSize: polyData.triangleStripsBufferSize,
triangleStrips: `data:application/vnd.itk.address,0:${triangleStripsPtr}`,
numberOfPointPixels: polyData.numberOfPointPixels,
pointData: `data:application/vnd.itk.address,0:${pointDataPtr}`,
numberOfCellPixels: polyData.numberOfCellPixels,
cellData: `data:application/vnd.itk.address,0:${cellDataPtr}`
};
setPipelineModuleInputJSON(pipelineModule, polyDataJSON, index);
break;
}
default:
throw Error('Unsupported input InterfaceType');
}
});
}
pipelineModule.resetModuleStdout();
pipelineModule.resetModuleStderr();
const stackPtr = pipelineModule.stackSave();
let returnValue = 0;
try {
returnValue = pipelineModule.callMain(args.slice());
}
catch (exception) {
// Note: Module must be built with CMAKE_BUILD_TYPE set to Debug.
// e.g.: itk-wasm build my/project -- -DCMAKE_BUILD_TYPE:STRING=Debug
if (typeof exception === 'number') {
console.log('Exception while running pipeline:');
console.log('stdout:', pipelineModule.getModuleStdout());
console.error('stderr:', pipelineModule.getModuleStderr());
if (typeof pipelineModule.getExceptionMessage !== 'undefined') {
console.error('exception:', pipelineModule.getExceptionMessage(exception));
}
else {
console.error('Build module in Debug mode for exception message information.');
}
}
throw exception;
}
finally {
pipelineModule.stackRestore(stackPtr);
}
const stdout = pipelineModule.getModuleStdout();
const stderr = pipelineModule.getModuleStderr();
const populatedOutputs = [];
if (!(outputs == null) && outputs.length > 0 && returnValue === 0) {
outputs.forEach(function (output, index) {
let outputData = null;
switch (output.type) {
case InterfaceTypes.TextStream: {
const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
const dataArrayView = new Uint8Array(pipelineModule.HEAPU8.buffer, dataPtr, dataSize);
outputData = { data: decoder.decode(dataArrayView) };
break;
}
case InterfaceTypes.JsonCompatible: {
const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
const dataArrayView = new Uint8Array(pipelineModule.HEAPU8.buffer, dataPtr, dataSize);
outputData = JSON.parse(decoder.decode(dataArrayView));
break;
}
case InterfaceTypes.BinaryStream: {
const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
outputData = {
data: memoryUint8SharedArray(pipelineModule, dataPtr, dataSize)
};
break;
}
case InterfaceTypes.TextFile: {
outputData = {
path: output.data.path,
data: pipelineModule.fs_readFile(output.data.path, {
encoding: 'utf8'
})
};
break;
}
case InterfaceTypes.BinaryFile: {
outputData = {
path: output.data.path,
data: readFileSharedArray(pipelineModule, output.data.path)
};
break;
}
case InterfaceTypes.Image: {
const image = getPipelineModuleOutputJSON(pipelineModule, index);
image.data = getPipelineModuleOutputArray(pipelineModule, index, 0, image.imageType.componentType);
image.direction = getPipelineModuleOutputArray(pipelineModule, index, 1, FloatTypes.Float64);
image.metadata = new Map(image.metadata);
outputData = image;
break;
}
case InterfaceTypes.Mesh: {
const mesh = getPipelineModuleOutputJSON(pipelineModule, index);
if (mesh.numberOfPoints > 0) {
mesh.points = getPipelineModuleOutputArray(pipelineModule, index, 0, mesh.meshType.pointComponentType);
}
else {
mesh.points = bufferToTypedArray(mesh.meshType.pointComponentType, new ArrayBuffer(0));
}
if (mesh.numberOfCells > 0) {
mesh.cells = getPipelineModuleOutputArray(pipelineModule, index, 1, mesh.meshType.cellComponentType);
}
else {
mesh.cells = bufferToTypedArray(mesh.meshType.cellComponentType, new ArrayBuffer(0));
}
if (mesh.numberOfPointPixels > 0) {
mesh.pointData = getPipelineModuleOutputArray(pipelineModule, index, 2, mesh.meshType.pointPixelComponentType);
}
else {
mesh.pointData = bufferToTypedArray(mesh.meshType.pointPixelComponentType, new ArrayBuffer(0));
}
if (mesh.numberOfCellPixels > 0) {
mesh.cellData = getPipelineModuleOutputArray(pipelineModule, index, 3, mesh.meshType.cellPixelComponentType);
}
else {
mesh.cellData = bufferToTypedArray(mesh.meshType.cellPixelComponentType, new ArrayBuffer(0));
}
outputData = mesh;
break;
}
case InterfaceTypes.PointSet: {
const pointSet = getPipelineModuleOutputJSON(pipelineModule, index);
if (pointSet.numberOfPoints > 0) {
pointSet.points = getPipelineModuleOutputArray(pipelineModule, index, 0, pointSet.pointSetType.pointComponentType);
}
else {
pointSet.points = bufferToTypedArray(pointSet.pointSetType.pointComponentType, new ArrayBuffer(0));
}
if (pointSet.numberOfPointPixels > 0) {
pointSet.pointData = getPipelineModuleOutputArray(pipelineModule, index, 1, pointSet.pointSetType.pointPixelComponentType);
}
else {
pointSet.pointData = bufferToTypedArray(pointSet.pointSetType.pointPixelComponentType, new ArrayBuffer(0));
}
outputData = pointSet;
break;
}
case InterfaceTypes.TransformList: {
const transformList = getPipelineModuleOutputJSON(pipelineModule, index);
transformList.forEach((transform, transformIndex) => {
if (transform.numberOfFixedParameters > 0) {
transformList[transformIndex].fixedParameters =
getPipelineModuleOutputArray(pipelineModule, index, transformIndex * 2, FloatTypes.Float64);
}
if (transform.numberOfParameters > 0) {
transformList[transformIndex].parameters =
getPipelineModuleOutputArray(pipelineModule, index, transformIndex * 2 + 1, transform.transformType.parametersValueType);
}
});
outputData = transformList;
break;
}
case InterfaceTypes.PolyData: {
const polyData = getPipelineModuleOutputJSON(pipelineModule, index);
if (polyData.numberOfPoints > 0) {
polyData.points = getPipelineModuleOutputArray(pipelineModule, index, 0, FloatTypes.Float32);
}
else {
polyData.points = new Float32Array();
}
if (polyData.verticesBufferSize > 0) {
polyData.vertices = getPipelineModuleOutputArray(pipelineModule, index, 1, IntTypes.UInt32);
}
else {
polyData.vertices = new Uint32Array();
}
if (polyData.linesBufferSize > 0) {
polyData.lines = getPipelineModuleOutputArray(pipelineModule, index, 2, IntTypes.UInt32);
}
else {
polyData.lines = new Uint32Array();
}
if (polyData.polygonsBufferSize > 0) {
polyData.polygons = getPipelineModuleOutputArray(pipelineModule, index, 3, IntTypes.UInt32);
}
else {
polyData.polygons = new Uint32Array();
}
if (polyData.triangleStripsBufferSize > 0) {
polyData.triangleStrips = getPipelineModuleOutputArray(pipelineModule, index, 4, IntTypes.UInt32);
}
else {
polyData.triangleStrips = new Uint32Array();
}
if (polyData.numberOfPointPixels > 0) {
polyData.pointData = getPipelineModuleOutputArray(pipelineModule, index, 5, polyData.polyDataType.pointPixelComponentType);
}
else {
polyData.pointData = bufferToTypedArray(polyData.polyDataType.pointPixelComponentType, new ArrayBuffer(0));
}
if (polyData.numberOfCellPixels > 0) {
polyData.cellData = getPipelineModuleOutputArray(pipelineModule, index, 6, polyData.polyDataType.cellPixelComponentType);
}
else {
polyData.cellData = bufferToTypedArray(polyData.polyDataType.cellPixelComponentType, new ArrayBuffer(0));
}
outputData = polyData;
break;
}
default:
throw Error('Unsupported output InterfaceType');
}
const populatedOutput = {
type: output.type,
data: outputData
};
populatedOutputs.push(populatedOutput);
});
}
return { returnValue, stdout, stderr, outputs: populatedOutputs };
}
export default runPipelineEmscripten;
//# sourceMappingURL=run-pipeline-emscripten.js.map