UNPKG

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
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