Hi everyone,
I need to know how to use vtk.js to load. NII image support. After consulting the official documentation. I should have successfully read the itk image data and passed it to the vtk.js renderer, but the page was unable to render any content. Here is my code
<template>
<div class="vtk-wrap">
<input type="file" accept=".nii,.gz,.dcm" @change="onFileChange" multiple />
<div
ref="vtkContainer"
id="vtkContainer"
style="width: 100%; height: 100%; position: absolute"
></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import '@kitware/vtk.js/Rendering/Profiles/Geometry'
import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow'
import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper'
import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume'
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera'
import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper'
import vtkSphereSource from '@kitware/vtk.js/Filters/Sources/SphereSource'
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'
import vtkConeSource from '@kitware/vtk.js/Filters/Sources/ConeSource'
import { readImage, niftiReadImage } from '@itk-wasm/image-io'
import { readImageDicomFileSeries } from '@itk-wasm/dicom'
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction'
let renderWindow: any = null
let renderer: any = null
let fullScreenRenderer: any = null
const vtkContainer = ref(null) as any
onMounted(() => {
if (!vtkContainer.value) {
console.error('vtkContainer is undefined')
return
}
fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
// background: [0, 0, 0],
// containerStyle: { height: '100%', width: '100%', position: 'absolute' },
container: vtkContainer.value
})
renderer = fullScreenRenderer.getRenderer()
renderWindow = fullScreenRenderer.getRenderWindow()
const interactor = renderWindow.getInteractor()
interactor.setInteractorStyle(vtkInteractorStyleTrackballCamera.newInstance())
renderWindow.getInteractor().setDesiredUpdateRate(15)
// 创建一个球体源
const sphereSource = vtkSphereSource.newInstance({
center: [0, 0, 0],
radius: 0.5,
thetaResolution: 30,
phiResolution: 30
})
// 创建一个 mapper
const mapper = vtkMapper.newInstance()
mapper.setInputConnection(sphereSource.getOutputPort())
// 创建一个 actor
const actor = vtkActor.newInstance()
actor.setMapper(mapper)
// 将 actor 添加到渲染器
renderer.addActor(actor)
renderer.resetCamera()
renderWindow.render()
console.log('Render window initialized')
})
async function loadFile(file: any): Promise<any> {
const fileType = file.name.split('.').pop()
console.log('Loading file:', file, fileType)
let vtkImage
if (fileType === 'nii' || fileType === 'gz') {
const itkImage = await readImage(file)
const itkImage1 = await niftiReadImage(file)
console.log('nii', itkImage, itkImage1)
vtkImage = vtkITKHelper.convertItkToVtkImage(itkImage.image)
} else if (fileType === 'dcm') {
const itkResult = await readImageDicomFileSeries({
inputImages: [file]
})
console.log('dcm', itkResult)
vtkImage = vtkITKHelper.convertItkToVtkImage(itkResult.outputImage)
} else {
console.error('Unsupported file type:', fileType)
return null
}
console.log(222, vtkImage)
return vtkImage
}
async function onFileChange(event: Event) {
const files = (event.target as HTMLInputElement).files
if (!files || files.length === 0) return
// 清理渲染器中的所有 actor 和 volume
// renderer.removeAllActors()
// renderer.removeAllVolumes()
for (let i = 0; i < files.length; i++) {
const file = files[i]
try {
const vtkImage = await loadFile(file)
console.log('VTK Image:', vtkImage)
if (!vtkImage) {
console.error('VTK Image data is invalid')
continue
}
const mapper = vtkVolumeMapper.newInstance()
mapper.setInputData(vtkImage)
const sampleDistance =
0.7 *
Math.sqrt(
vtkImage
.getSpacing()
.map((v: number) => v * v)
.reduce((a: any, b: any) => a + b, 0)
)
mapper.setSampleDistance(sampleDistance)
const actor = vtkVolume.newInstance()
actor.setMapper(mapper)
renderer.addVolume(actor)
const lookupTable = vtkColorTransferFunction.newInstance()
// 根据图像数据分布调整lookupTable
lookupTable.addRGBPoint(0, 0.0, 0.0, 0.0)
lookupTable.addRGBPoint(500, 1.0, 0.5, 0.3)
lookupTable.addRGBPoint(1000, 1.0, 0.9, 0.7)
const piecewiseFunction = vtkPiecewiseFunction.newInstance()
// 根据图像数据分布调整piecewiseFunction
piecewiseFunction.addPoint(0, 0.0)
piecewiseFunction.addPoint(500, 0.6)
piecewiseFunction.addPoint(1000, 1.0)
actor.getProperty().setRGBTransferFunction(0, lookupTable)
actor.getProperty().setScalarOpacity(0, piecewiseFunction)
actor.getProperty().setInterpolationTypeToLinear()
// actor.getProperty().setInterpolationTypeToNearest()
// const volume = vtkVolume.newInstance()
// volume.setMapper(mapper)
// renderer.addVolume(volume)
renderer.resetCamera()
renderer.getActiveCamera().elevation(30)
renderer.getActiveCamera().orthogonalizeViewUp()
renderWindow.render()
} catch (error) {
console.error('Error loading NIfTI file:', error)
}
}
console.log('Rendering completed')
}
</script>
<style scoped>
.vtk-wrap {
position: relative;
width: 800px;
height: 600px;
}
</style>
"dependencies": {
"@itk-wasm/dicom": "^7.0.0",
"@itk-wasm/image-io": "^1.2.0",
"@kitware/vtk.js": "^30.5.1",
"itk": "^14.1.1",
"itk-wasm": "^1.0.0-b.175",
"vue": "^3.4.21"
},