UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

494 lines (479 loc) 17 kB
import { m as macro } from '../../macros2.js'; import vtkImageData from './ImageData.js'; import vtkPolyData from './PolyData.js'; import vtkDataArray from '../Core/DataArray.js'; const { vtkErrorMacro } = macro; // see itk.js PixelTypes.js const ITKJSPixelTypes = { Unknown: 0, Scalar: 1, RGB: 2, RGBA: 3, Offset: 4, Vector: 5, Point: 6, CovariantVector: 7, SymmetricSecondRankTensor: 8, DiffusionTensor3D: 9, Complex: 10, FixedArray: 11, Array: 12, Matrix: 13, VariableLengthVector: 14, VariableSizeMatrix: 15 }; // itk-wasm pixel types from https://github.com/InsightSoftwareConsortium/itk-wasm/blob/master/src/core/PixelTypes.ts const ITKWASMPixelTypes = { Unknown: 'Unknown', Scalar: 'Scalar', RGB: 'RGB', RGBA: 'RGBA', Offset: 'Offset', Vector: 'Vector', Point: 'Point', CovariantVector: 'CovariantVector', SymmetricSecondRankTensor: 'SymmetricSecondRankTensor', DiffusionTensor3D: 'DiffusionTensor3D', Complex: 'Complex', FixedArray: 'FixedArray', Array: 'Array', Matrix: 'Matrix', VariableLengthVector: 'VariableLengthVector', VariableSizeMatrix: 'VariableSizeMatrix' }; const vtkArrayTypeToItkComponentType = new Map([['Uint8Array', 'uint8'], ['Int8Array', 'int8'], ['Uint16Array', 'uint16'], ['Int16Array', 'int16'], ['Uint32Array', 'uint32'], ['Int32Array', 'int32'], ['Float32Array', 'float32'], ['Float64Array', 'float64']]); const itkComponentTypeToVtkArrayType = new Map([['uint8', 'Uint8Array'], ['int8', 'Int8Array'], ['uint16', 'Uint16Array'], ['int16', 'Int16Array'], ['uint32', 'Uint32Array'], ['int32', 'Int32Array'], ['float32', 'Float32Array'], ['float64', 'Float64Array']]); /** * Converts an itk-wasm Image to a vtk.js vtkImageData. * * Requires an itk-wasm Image as input. */ function convertItkToVtkImage(itkImage, options = {}) { const vtkImage = { origin: [0, 0, 0], spacing: [1, 1, 1] }; const dimensions = [1, 1, 1]; const direction = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Check whether itkImage is an itk.js Image or an itk-wasm Image? const isITKWasm = itkImage.direction.data === undefined; const ITKPixelTypes = isITKWasm ? ITKWASMPixelTypes : ITKJSPixelTypes; for (let idx = 0; idx < itkImage.imageType.dimension; ++idx) { vtkImage.origin[idx] = itkImage.origin[idx]; vtkImage.spacing[idx] = itkImage.spacing[idx]; dimensions[idx] = itkImage.size[idx]; for (let col = 0; col < itkImage.imageType.dimension; ++col) { // ITK (and VTKMath) use a row-major index axis, but the direction // matrix on the vtkImageData is a webGL matrix, which uses a // column-major data layout. Transpose the direction matrix from // itkImage when instantiating that vtkImageData direction matrix. if (isITKWasm) { direction[col + idx * 3] = itkImage.direction[idx + col * itkImage.imageType.dimension]; } else { direction[col + idx * 3] = itkImage.direction.data[idx + col * itkImage.imageType.dimension]; } } } // Create VTK Image Data const imageData = vtkImageData.newInstance(vtkImage); // Create VTK point data -- the data associated with the pixels / voxels const pointData = vtkDataArray.newInstance({ name: options.scalarArrayName || 'Scalars', values: itkImage.data, numberOfComponents: itkImage.imageType.components }); imageData.setDirection(direction); imageData.setDimensions(...dimensions); // Always associate multi-component pixel types with vtk.js point data // scalars to facilitate multi-component volume rendering imageData.getPointData().setScalars(pointData); // Associate the point data that are 3D vectors / tensors // Refer to itk-js/src/PixelTypes.js for numerical values switch (isITKWasm ? ITKPixelTypes[itkImage.imageType.pixelType] : itkImage.imageType.pixelType) { case ITKPixelTypes.Scalar: break; case ITKPixelTypes.RGB: break; case ITKPixelTypes.RGBA: break; case ITKPixelTypes.Offset: break; case ITKPixelTypes.Vector: if (itkImage.imageType.dimension === 3 && itkImage.imageType.components === 3) { imageData.getPointData().setVectors(pointData); } break; case ITKPixelTypes.Point: break; case ITKPixelTypes.CovariantVector: if (itkImage.imageType.dimension === 3 && itkImage.imageType.components === 3) { imageData.getPointData().setVectors(pointData); } break; case ITKPixelTypes.SymmetricSecondRankTensor: if (itkImage.imageType.dimension === 3 && itkImage.imageType.components === 6) { imageData.getPointData().setTensors(pointData); } break; case ITKPixelTypes.DiffusionTensor3D: if (itkImage.imageType.dimension === 3 && itkImage.imageType.components === 6) { imageData.getPointData().setTensors(pointData); } break; case ITKPixelTypes.Complex: break; case ITKPixelTypes.FixedArray: break; case ITKPixelTypes.Array: break; case ITKPixelTypes.Matrix: break; case ITKPixelTypes.VariableLengthVector: break; case ITKPixelTypes.VariableSizeMatrix: break; default: vtkErrorMacro(`Cannot handle unexpected itk-wasm pixel type ${itkImage.imageType.pixelType}`); return null; } return imageData; } /** * Converts a vtk.js vtkImageData to an itk-wasm Image. * * Requires a vtk.js vtkImageData as input. * */ function convertVtkToItkImage(vtkImage, copyData = false) { const dimension = 3; const itkImage = { imageType: { dimension, pixelType: ITKWASMPixelTypes.Scalar, componentType: '', components: 1 }, name: 'vtkImageData', origin: vtkImage.getOrigin(), spacing: vtkImage.getSpacing(), direction: new Float64Array(9), size: vtkImage.getDimensions() }; const direction = vtkImage.getDirection(); // Transpose the direction matrix from column-major to row-major for (let idx = 0; idx < dimension; ++idx) { for (let idy = 0; idy < dimension; ++idy) { itkImage.direction[idx + idy * dimension] = direction[idy + idx * dimension]; } } const pointData = vtkImage.getPointData(); let vtkArray; if (pointData.getTensors() !== null) { itkImage.imageType.pixelType = ITKWASMPixelTypes.DiffusionTensor3D; vtkArray = pointData.getTensors(); } else if (pointData.getVectors() != null) { itkImage.imageType.pixelType = ITKWASMPixelTypes.Vector; vtkArray = pointData.getVectors(); } else { vtkArray = pointData.getScalars(); } itkImage.imageType.componentType = vtkArrayTypeToItkComponentType.get(vtkArray.getDataType()); if (copyData) { // Copy the data array itkImage.data = vtkArray.getData().slice(0); } else { itkImage.data = vtkArray.getData(); } return itkImage; } /** * Converts an itk-wasm PolyData to a vtk.js vtkPolyData. * * Requires an itk-wasm PolyData as input. */ function convertItkToVtkPolyData(itkPolyData, options = {}) { const pointDataArrays = []; if (itkPolyData.pointData.length) { pointDataArrays.push({ data: { vtkClass: 'vtkDataArray', name: options.pointDataName || 'PointData', numberOfComponents: itkPolyData.polyDataType.pointPixelComponents, size: itkPolyData.pointData.length, dataType: itkComponentTypeToVtkArrayType.get(itkPolyData.polyDataType.pointPixelComponentType), buffer: itkPolyData.pointData.buffer, values: itkPolyData.pointData } }); } const cellDataArrays = []; if (itkPolyData.cellData.length) { cellDataArrays.push({ data: { vtkClass: 'vtkDataArray', name: options.cellDataName || 'CellData', numberOfComponents: itkPolyData.polyDataType.pointPixelComponents, size: itkPolyData.cellData.length, dataType: itkComponentTypeToVtkArrayType.get(itkPolyData.polyDataType.pointPixelComponentType), buffer: itkPolyData.cellData.buffer, values: itkPolyData.cellData } }); } const vtkPolyDataModel = { points: { vtkClass: 'vtkPoints', name: '_points', numberOfComponents: 3, size: itkPolyData.points.length, dataType: 'Float32Array', buffer: itkPolyData.points.buffer, values: itkPolyData.points }, verts: { vtkClass: 'vtkCellArray', name: '_verts', numberOfComponents: 1, size: itkPolyData.verticesBufferSize, dataType: 'Uint32Array', buffer: itkPolyData.vertices.buffer, values: itkPolyData.vertices }, lines: { vtkClass: 'vtkCellArray', name: '_lines', numberOfComponents: 1, size: itkPolyData.linesBufferSize, dataType: 'Uint32Array', buffer: itkPolyData.lines.buffer, values: itkPolyData.lines }, polys: { vtkClass: 'vtkCellArray', name: '_polys', numberOfComponents: 1, size: itkPolyData.polygonsBufferSize, dataType: 'Uint32Array', buffer: itkPolyData.polygons.buffer, values: itkPolyData.polygons }, strips: { vtkClass: 'vtkCellArray', name: '_strips', numberOfComponents: 1, size: itkPolyData.triangleStripsBufferSize, dataType: 'Uint32Array', buffer: itkPolyData.triangleStrips.buffer, values: itkPolyData.triangleStrips }, pointData: { vtkClass: 'vtkDataSetAttributes', activeGlobalIds: -1, activeNormals: -1, activePedigreeIds: -1, activeScalars: -1, activeTCoords: -1, activeTensors: -1, activeVectors: -1, copyFieldFlags: [], doCopyAllOff: false, doCopyAllOn: true, arrays: pointDataArrays }, cellData: { vtkClass: 'vtkDataSetAttributes', activeGlobalIds: -1, activeNormals: -1, activePedigreeIds: -1, activeScalars: -1, activeTCoords: -1, activeTensors: -1, activeVectors: -1, copyFieldFlags: [], doCopyAllOff: false, doCopyAllOn: true, arrays: cellDataArrays } }; // Create VTK PolyData const polyData = vtkPolyData.newInstance(vtkPolyDataModel); const pd = polyData.getPointData(); const cd = polyData.getCellData(); if (itkPolyData.pointData.length) { // Associate the point data that are 3D vectors / tensors switch (ITKWASMPixelTypes[itkPolyData.polyDataType.pointPixelType]) { case ITKWASMPixelTypes.Scalar: pd.setScalars(pd.getArrayByIndex(0)); break; case ITKWASMPixelTypes.RGB: break; case ITKWASMPixelTypes.RGBA: break; case ITKWASMPixelTypes.Offset: break; case ITKWASMPixelTypes.Vector: if (itkPolyData.polyDataType.pointPixelComponents === 3) { pd.setVectors(pd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.Point: break; case ITKWASMPixelTypes.CovariantVector: if (itkPolyData.polyDataType.pointPixelComponents === 3) { pd.setVectors(pd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.SymmetricSecondRankTensor: if (itkPolyData.polyDataType.pointPixelComponents === 6) { pd.setTensors(pd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.DiffusionTensor3D: if (itkPolyData.polyDataType.pointPixelComponents === 6) { pd.setTensors(pd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.Complex: break; case ITKWASMPixelTypes.FixedArray: break; case ITKWASMPixelTypes.Array: break; case ITKWASMPixelTypes.Matrix: break; case ITKWASMPixelTypes.VariableLengthVector: break; case ITKWASMPixelTypes.VariableSizeMatrix: break; default: vtkErrorMacro(`Cannot handle unexpected itk-wasm pixel type ${itkPolyData.polyDataType.pointPixelType}`); return null; } } if (itkPolyData.cellData.length) { // Associate the cell data that are 3D vectors / tensors switch (ITKWASMPixelTypes[itkPolyData.polyDataType.cellPixelType]) { case ITKWASMPixelTypes.Scalar: cd.setScalars(cd.getArrayByIndex(0)); break; case ITKWASMPixelTypes.RGB: break; case ITKWASMPixelTypes.RGBA: break; case ITKWASMPixelTypes.Offset: break; case ITKWASMPixelTypes.Vector: if (itkPolyData.polyDataType.pointPixelComponents === 3) { cd.setVectors(cd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.Point: break; case ITKWASMPixelTypes.CovariantVector: if (itkPolyData.polyDataType.pointPixelComponents === 3) { cd.setVectors(cd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.SymmetricSecondRankTensor: if (itkPolyData.polyDataType.pointPixelComponents === 6) { cd.setTensors(cd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.DiffusionTensor3D: if (itkPolyData.polyDataType.pointPixelComponents === 6) { cd.setTensors(cd.getArrayByIndex(0)); } break; case ITKWASMPixelTypes.Complex: break; case ITKWASMPixelTypes.FixedArray: break; case ITKWASMPixelTypes.Array: break; case ITKWASMPixelTypes.Matrix: break; case ITKWASMPixelTypes.VariableLengthVector: break; case ITKWASMPixelTypes.VariableSizeMatrix: break; default: vtkErrorMacro(`Cannot handle unexpected itk-wasm pixel type ${itkPolyData.polyDataType.pointPixelType}`); return null; } } return polyData; } /** * Converts a vtk.js vtkPolyData to an itk-wasm PolyData. * * Requires a vtk.js vtkPolyData as input. * */ function convertVtkToItkPolyData(polyData, options = {}) { const itkPolyData = { polyDataType: { pointPixelComponentType: 'float32', pointPixelComponents: 1, pointPixelType: 'Scalar', cellPixelComponentType: 'float32', cellPixelComponents: 1, cellPixelType: 'Scalar' }, numberOfPoints: polyData.getNumberOfPoints(), points: polyData.getPoints().getData(), verticesBufferSize: polyData.getVerts().getNumberOfValues(), vertices: polyData.getVerts().getData(), linesBufferSize: polyData.getLines().getNumberOfValues(), lines: polyData.getLines().getData(), polygonsBufferSize: polyData.getPolys().getNumberOfValues(), polygons: polyData.getPolys().getData(), triangleStripsBufferSize: polyData.getStrips().getNumberOfValues(), triangleStrips: polyData.getStrips().getData(), numberOfPointPixels: 0, pointData: new Float32Array(), numberOfCellPixels: 0, cellData: new Float32Array() }; const pd = polyData.getPointData(); if (pd.getNumberOfArrays()) { const pdArray = options.pointDataName ? pd.getArrayByName(options.pointDataName) : pd.getArrayByIndex(0); itkPolyData.numberOfPointPixels = pdArray.getNumberOfTuples(); itkPolyData.pointData = pdArray.getData(); itkPolyData.polyDataType.pointPixelComponentType = vtkArrayTypeToItkComponentType.get(pdArray.getDataType()); // default to the same type itkPolyData.polyDataType.cellPixelComponentType = itkPolyData.polyDataType.pointPixelComponentType; itkPolyData.polyDataType.pointPixelComponents = pdArray.getNumberOfComponents(); itkPolyData.polyDataType.cellPixelComponents = itkPolyData.polyDataType.pointPixelComponents; if (pd.getTensors() === pdArray) { itkPolyData.polyDataType.pointPixelType = ITKWASMPixelTypes.SymmetricSecondRankTensor; } else if (pd.getVectors() === pdArray) { itkPolyData.polyDataType.pointPixelType = ITKWASMPixelTypes.Vector; } itkPolyData.polyDataType.cellPixelType = itkPolyData.polyDataType.pointPixelType; } const cd = polyData.getCellData(); if (cd.getNumberOfArrays()) { const cdArray = options.cellDataName ? pd.getArrayByName(options.cellDataName) : pd.getArrayByIndex(0); itkPolyData.numberOfCellPixels = cdArray.getNumberOfTuples(); itkPolyData.cellData = cdArray.getData(); itkPolyData.polyDataType.cellPixelComponentType = vtkArrayTypeToItkComponentType.get(cdArray.getDataType()); itkPolyData.polyDataType.cellPixelComponents = cdArray.getNumberOfComponents(); if (cd.getTensors() === cdArray) { itkPolyData.polyDataType.cellPixelType = ITKWASMPixelTypes.SymmetricSecondRankTensor; } else if (cd.getVectors() === cdArray) { itkPolyData.polyDataType.cellPixelType = ITKWASMPixelTypes.Vector; } else { itkPolyData.polyDataType.cellPixelType = ITKWASMPixelTypes.Scalar; } } return itkPolyData; } var ITKHelper = { convertItkToVtkImage, convertVtkToItkImage, convertItkToVtkPolyData, convertVtkToItkPolyData }; export { convertItkToVtkImage, convertItkToVtkPolyData, convertVtkToItkImage, convertVtkToItkPolyData, ITKHelper as default };