Web3D 2013 Tutorial:
Medical and Volume Rendering with X3D
Nicholas F. Polys, Ph.D. Andy Wood Virginia Tech Peter Leskovsky, John Congote, Esther Novo, Luis Kabongo, Ph.D. VicomTech
Topics Scenarios and Motivation Image Formats Processing Pipelines - - PowerPoint PPT Presentation
Web3D 2013 Tutorial : Medical and Volume Rendering with X3D Nicholas F. Polys, Ph.D. Andy Wood Virginia Tech Peter Leskovsky, John Congote, Esther Novo, Luis Kabongo, Ph.D. VicomTech Topics Scenarios and Motivation Image Formats
Nicholas F. Polys, Ph.D. Andy Wood Virginia Tech Peter Leskovsky, John Congote, Esther Novo, Luis Kabongo, Ph.D. VicomTech
– Sample xxxxx.dcm – X3D Content Examples http://www.web3d.org/x3d/content/examples/Basic/VolumeRendering/index.html – Volvis.org – http://www.osirix-viewer.com/datasets/
– ImageJ : http://rsbweb.nih.gov/ij/
– Seg3D.org – Slicer.org; ITK-Snap – X3D-Edit 3.3
– H3D.org – InstantReality.org – MedX3DOM
– Formalizes parameters and transfer functions for 3D rendering & blending
– Greatest Common Denominator
– BlendedVolumeStyle – SegmentedVolumeData – IsoSurfaceVolumeData
Silhouette Opacity Map Cartoon
– Core – Time – Networking – Grouping – Rendering – Shape – Geometry3D – Geometry2D – Text – Lighting – Texturing – Interpolation – Navigation – Environmental effects – Event utilities – Texturing3D – Volume rendering Includes polygon, line and point rendering; metadata on any node
Vis-X3D-collected.mp4 (65 mb)
I: Scenarios and Motivation
(Head MRI, XML encoding) <Transform DEF='backdrop' > <VolumeData dimensions='.75 1 1' >
<ImageTexture3D containerField="voxels" url='"./Segments/masked-vispart.nrrd"'/>
<OpacityMapVolumeStyle /> </VolumeData> </Transform>
(Head MRI, optic segment) <ISOSurfaceVolumeData surfaceValues='.15' dimensions='.75 1 1' > <ImageTexture3D containerField="voxels" url='"./Segments/masked-optic.nrrd"'/> <CartoonVolumeStyle /> </ISOSurfaceVolumeData> (Head MRI, cerebrum segment)
<VolumeData dimensions='.75 1 1' > <ImageTexture3D containerField="voxels" url='"./Segments/masked-cerebrum.nrrd"'/> <ComposedVolumeStyle> <CartoonVolumeStyle /> <EdgeEnhancementVolumeStyle gradientThreshold='.8' edgeColor='0 0 .5' /> </ComposedVolumeStyle> </VolumeData>
I: Scenarios and Motivation
III: Processing Pipelines
II: Image Formats
III: Processing Pipelines
Volume acquisition DICOM data Surfacing Viewer workstation Compose WCS Model (Scene) for Rendering; assign appearances, views, etc. Segmentation nDPS
Segmentation: Supp 111 Mesh: Supp 132 Other 3D data
(e.g. CAD, X3D)
Source Data X3D
<ImageTexture2D> <VolumeData>
<ImageTexture3D> -voxels
<IsoSurfaceVolumeData>
<ImageTexture3D> -voxels <SegmentedVolumeData> <ImageTexture3D> -voxels <ImageTexture3D>
Image Processor (ImageJ) Volume Data Processing (TEEM) Segmentation
DICOM Plugin Image Stack DICOM Stack Raw Volume NRRD Volume
Segment IDs Individual Segments
Source Data X3D
<ImageTexture2D> <VolumeData>
<ImageTexture3D> -voxels
<IsoSurfaceVolumeData>
<ImageTexture3D> -voxels <SegmentedVolumeData> <ImageTexture3D> -voxels <ImageTexture3D>
Image Processor (ImageJ) Volume Data Processing (TEEM) Segmentation
DICOM Plugin Image Stack DICOM Stack Raw Volume NRRD Volume
Segment IDs Individual Segments
volume stack is single channel (greyscale)
– 512 512x512 8-bit greyscale images: 134 million voxels and 128MB (raw) – 1024^3 volume, 16-bit RGB: 1.07 billion voxels and 48GB (raw)
– Image processor designed to work with stacks – Many useful (and scriptable) functions: invert, blur, resample, resize, mask, etc. – Website: http://rsbweb.nih.gov/ij/
Source Data X3D
<ImageTexture2D> <VolumeData>
<ImageTexture3D> -voxels
<IsoSurfaceVolumeData>
<ImageTexture3D> -voxels <SegmentedVolumeData> <ImageTexture3D> -voxels <ImageTexture3D>
Image Processor (ImageJ) Volume Data Processing (TEEM) Segmentation
DICOM Plugin Image Stack DICOM Stack Raw Volume NRRD Volume
Segment IDs Individual Segments
Source Data X3D
<ImageTexture2D> <VolumeData>
<ImageTexture3D> -voxels
<IsoSurfaceVolumeData>
<ImageTexture3D> -voxels <SegmentedVolumeData> <ImageTexture3D> -voxels <ImageTexture3D>
Image Processor (ImageJ) Volume Data Processing (TEEM) Segmentation
DICOM Plugin Image Stack DICOM Stack Raw Volume NRRD Volume
Segment IDs Individual Segments
– Large selection of segmentation algorithms and tools – Layered segment masks with multi-layer operations and export flexibility – http://www.sci.utah.edu/cibc-software/seg3d.html
– Active contour segmentation (volume growing) and manual tools – http://www.itksnap.org/pmwiki/pmwiki.php
– Volume manipulation and segmentation, with a focus on registration (multiple volumes) and rendering – http://slicer.org/
Source Data X3D
<ImageTexture2D> <VolumeData>
<ImageTexture3D> -voxels
<IsoSurfaceVolumeData>
<ImageTexture3D> -voxels <SegmentedVolumeData> <ImageTexture3D> -voxels <ImageTexture3D>
Image Processor (ImageJ) Volume Data Processing (TEEM) Segmentation
DICOM Plugin Image Stack DICOM Stack Raw Volume NRRD Volume
Segment IDs Individual Segments
<VolumeData dimensions='1.28 1.28 1.0' > <!-- VolumeRenderStyle node here (optional) --> <ImageTexture3D containerField='voxels' url=' "path_to_dataset" '/> </VolumeData>
<IsoSurfaceVolumeData dimensions='1.28 1.28 1.28‘ surfaceValues='.15' contourStepSize='0' surfaceTolerance='0' containerField='children'> <CartoonVolumeStyle colorSteps='32' /> <ImageTexture3D containerField='voxels' url=' "skull.nrrd" '/> </IsoSurfaceVolumeData>
– Two or three dimensional texture – One to four components
<OpacityMapVolumeStyle> <ImageTexture3D containerField='transferFunction' url='"engineTransferSchnitt.png"' /> </OpacityMapVolumeStyle>
Default With Transfer Function
MIN, or AVERAGE of the voxel values along the ray
above/below the threshol
<ProjectionVolumeStyle type='MAX' enabled='true' intensityThreshold='0' containerField='renderStyle'/>
Max Projection Threshold Max Default Style
Opacity Map Edge Enhanced Cartoon Shaded Tone Map
<BoundaryEnhancementVolumeStyle boundaryOpacity='0.9' opacityFactor='0.9' retainedOpacity='0.2'>
Default Boundary Enhanced
<EdgeEnhancementVolumeStyle enabled='true' edgeColor=‘1 0 0 1‘ gradientThreshold='0.4' containerField='renderStyle'/>
Default Edge Enhanced
<SilhouetteEnhancementVolumeStyle silhouetteBoundaryOpacity='1' silhouetteRetainedOpacity='.5' silhouetteSharpness='10' enabled='true' containerField='renderStyle'/>
Default Silhouette Enhanced
number of color steps between an orthogonal (plane surface) color and parallel color:
<CartoonVolumeStyle enabled='true' colorSteps='4' orthogonalColor='1 1 1 1' parallelColor='0 0 0 1' containerField='renderStyle'/>
2 color steps 4 color steps 8 color steps
geometry (relative to light source)
<ShadedVolumeStyle lighting='true' shadows='true' enabled='true' phaseFunction='Henyey-Greenstein' containerField='renderStyle'> <Material ambientIntensity='0.8' diffuseColor='0 .5 1‘ shininess='0.08' specularColor='1 1 1'/> </ShadedVolumeStyle>
Default Shaded
<ToneMappedVolumeStyle warmColor='0 0 1 0' coolColor='1 1 0 0' />
Default Tone Mapped
<ComposedVolumeStyle enabled='true' ordered='false' containerField='renderStyle'> <SilhouetteEnhancementVolumeStyle silhouetteBoundaryOpacity='1' silhouetteRetainedOpacity='.1' silhouetteSharpness='10' enabled='true' containerField='renderStyle'/> <EdgeEnhancementVolumeStyle edgeColor='.5 0 0' gradientThreshold='.8' enabled='true' containerField='renderStyle'/> </ComposedVolumeStyle>
Style1 (Edge Enhance) Style2 (Silhouette) Composed Styles
<SegmentedVolumeData dimensions='2.304 2.304 1.116' containerField='children'> <ImageTexture3D containerField='voxels' repeatS='false' repeatT='false' repeatR='false' url=' "mri_ventricles.nrrd" '/> <ImageTexture3D containerField='segmentIdentifiers' repeatS='false' repeatT='false' repeatR='false' url=' "mri_ventricles_segment.nrrd" '/> <OpacityMapVolumeStyle enabled='true' containerField='renderStyle'/> <ToneMappedVolumeStyle enabled='true' coolColor='0 0 1 0' warmColor='1 1 0 0' containerField='renderStyle'/> </SegmentedVolumeData>
Voxels Segments Styles Default Segmented Volume
Segment1 Segment2 Style 1 Style 2 <VolumeData dimensions='.75 1 1' > <ImageTexture3D containerField="voxels" url='"./Segments/masked-halfhead.nrrd"'/> <OpacityMapVolumeStyle /> </VolumeData> <VolumeData dimensions='.75 1 1' > <ImageTexture3D containerField="voxels" url='"./Segments/masked-cerebrum.nrrd"'/> <ShadedVolumeStyle lighting="TRUE" shadows="TRUE" > <Material diffuseColor='0 .5 1' specularColor='1 1 1' ambientIntensity='0.8' shininess='0.08' /> </ShadedVolumeStyle> </VolumeData>
1 2 3 4 5
Composited Volume
Volume1 Volume2 Style2 Default Style1 Volume1 (Default) Volume2 (Tone Map) Blended Volume <VolumeData dimensions='512 512 452' containerField='children'> <BlendedVolumeStyle weightConstant1='0.51' enabled='true' weightConstant2='0.5' weightFunction1='CONSTANT' weightFunction2='CONSTANT' containerField='renderStyle'> <ToneMappedVolumeStyle enabled='true' coolColor='0 0 1 0' warmColor='1 1 0 0' containerField='renderStyle'/> <ImageTexture3D containerField='voxels' repeatS='false' repeatT='false' repeatR='false' url=' "internals.nrrd" '/> </BlendedVolumeStyle> <ImageTexture3D containerField='voxels' repeatS='false' repeatT='false' repeatR='false' url=' "body.nrrd" '/> </VolumeData>
N= 50
<HTML><HEAD><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <TITLE>Real-Time Interactive Visualization of Volumetric Data with WebGL</TITLE> <SCRIPT type="text/javascript" src="sylvester.js"></SCRIPT> <SCRIPT type="text/javascript" src="glUtils.js"></SCRIPT> <SCRIPT type="text/javascript" src="glAux.js"></SCRIPT> <SCRIPT type="text/javascript" src="volumerc.js"></SCRIPT> </HEAD><BODY onload="volumerc_main('./raycast-color.frag','./aorta- low.jpg','./tf.png');" bgcolor="white"> <CENTER> <H1>Real-Time Interactive Visualization of Volumetric Data with WebGL</H1> <canvas id="canvas_win" width="512" height="512" style="border:1px solid black"></canvas> </br> <button type="button" onclick="volumerc_main('./raycast- bw.frag','./aorta-low.jpg','./tf.png');">Aorta LowRes B/W</button> <button type="button" onclick="volumerc_main('./raycast- color.frag','./aorta-low.jpg','./tf.png');">Aorta LowRes Color</button> <button type="button" onclick="volumerc_main('./raycast- bw.frag','./aorta-high.jpg','./tf.png');">Aorta HighRes B/W</button> <button type="button" onclick="volumerc_main('./raycast- color.frag','./aorta-high.jpg','./tf.png');">Aorta HighRes Color</button> <P>Rotation with the mouse, and Alt+Mouse to Zoom</P> </BR> …
Node generation for two components
Implemented/In progress Defined Not implemented Abstract
Ex: Brain Atlas Image The texture atlas is the mosaic of all the slices of a volume in one image, the order of the images is given by the rows and columns.
ImageTextureAtlas: ImageTexture {
SFInt32 [in] numberOfSlices 0 SFInt32 [in] slicesOverX 0 SFInt32 [in] slicesOverY 0
}
OpacityMapVolumeStyle : X3DComposableVolumeRenderStyleNode {
SFBool [in,out] enabled TRUE SFNode [in,out] metadata NULL [X3DMetadataObject] SFNode [in,out] transferFunction NULL [X3DTexture2DNode,X3DTexture3DNode] SFFloat [in,out] opacityFactor 0.01 [0,1] SFFloat [in,out] lightFactor 0.3 [0,1]
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="X-UA-Compatible" content="chrome=1" /> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Hello World</title> <link rel="stylesheet" type="text/css" href="x3dom.css" /> <script type="text/javascript" src="x3dom.js"></script> </head> <body> <h1>XHTML Hello World</h1> <p>With X3D-namespace and case senstive element/node names. Works with self-closing tags (e.g. Viewpoint and Material)</p> <X3D xmlns="http://www.web3d.org/specifications/x3d-namespace" showStat="false" showLog="false" x="0px" y="0px" width="400px" height="400px“ altImg="helloX3D-alt.png"> <Scene> <Viewpoint position='0 0 10' /> <Shape> <Appearance> <Material diffuseColor='0.603 0.894 0.909' /> </Appearance> <Box DEF='box'/> </Shape> </Scene> </X3D> </body> </html>
<?xml version="1.0" encoding="utf-8"?> <X3D version="3.2" profile="MedicalInterchange"> <Scene> <Group> <NavigationInfo type='"EXAMINE" "WALK"'/> <Viewpoint description="first" position="0 0 0.6"/> <Background skyColor="1 1 1"/> <Transform scale=".001 .001 .001"> <!-- The volume --> <VolumeData DEF="volume” rayStep="0.01“ useSlicing="false“ dimensions="250 250 156"> <Image3DTexture containerField="voxels“ url='"data/S76280/IM00010“> </VolumeData> </Transform> </Group> </Scene> </X3D>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN“ "http://www.w3.org/TR/xhtml1/DTD/xhtml1- strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="X-UA-Compatible" content="chrome=1" /> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <title>Hello World</title> <link rel="stylesheet" type="text/css" href="x3dom.css" /> <script type="text/javascript" src="x3dom.js"></script> </head> <body> <X3D xmlns='http://www.web3d.org/specifications/x3d-namespace' showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.3 0.65'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/aorta4096.png' numberOfSlices='96' slicesOverX='10' slicesOverY='10'/> <OpacityMapVolumeStyle lightFactor='0.01'> <ImageTexture containerField='transferFunction' url='transfer.png'/> </OpacityMapVolumeStyle> </VolumeData> </Transform> </Scene> </X3D> </body> </html>
<X3D xmlns='http://www.web3d.org/specifications/x3d-namespace’ showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/aorta4096.png' numberOfSlices='96' slicesOverX='10' slicesOverY='10'/> <OpacityMapVolumeStyle lightFactor='0.02'
<ImageTexture containerField='transferFunction' url='media/volume/transfer/transfer.png'/> </OpacityMapVolumeStyle> </VolumeData> </Transform> </Scene> </X3D>
http://x3dom.org/x3dom/test/functional/volrenOpacityTestTF_aorta.xhtml
<X3D xmlns='http://www.web3d.org/specifications/x3d- namespace' showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/skull-at.png' numberOfSlices='256' slicesOverX='16' slicesOverY='16'/> <OpacityMapVolumeStyle> </OpacityMapVolumeStyle> </VolumeData> </Transform> </Scene> </X3D>
<X3D xmlns='http://www.web3d.org/specifications/x3d- namespace' showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/brain-at_4096.jpg' numberOfSlices='226' slicesOverX='16' slicesOverY='16'/> <OpacityMapVolumeStyle> </OpacityMapVolumeStyle> </VolumeData> </Transform> </Scene> </X3D>
<X3D xmlns='http://www.web3d.org/specifications/x3d- namespace' showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/aorta4096.png' numberOfSlices=‘96' slicesOverX='10' slicesOverY=‘10'/> <OpacityMapVolumeStyle> </OpacityMapVolumeStyle> </VolumeData> </Transform> </Scene> </X3D>
<X3D xmlns='http://www.web3d.org/specifications/x3d- namespace’ showStat='true' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100’ position="0.85713 1.49578 9.85028"
<Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/aorta4096.png’ numberOfSlices='96' slicesOverX='10' slicesOverY='10'/> <MPRVolumeStyle id='style' positionLine='0.5'> </MPRVolumeStyle> </VolumeData> </Transform> </Scene> </X3D>
http://x3dom.org/x3dom/test/functional/volrenMPR_aorta.xhtml
http://www.volumerc.org/documentation.html
– Parses all slices in a folder and merges then into a PNG atlas at different resolutions (full, 81922, 40962, 20482, 10242) – Automatic data rescale – DICOM compatibility, uses Window/Level values (requires pydicom and numpy)
format:
– Implement loadMyData function
Not optimised!! (memory)
Usage: command <InputFolder> <OutputFilename>
PNG files to be processed
base name of the desired output, extension will be added automatically
/* ### ISOSurfaceVolumeData ### */ x3dom.registerNodeType( "ISOSurfaceVolumeData", "VolumeRendering", defineClass(x3dom.nodeTypes.X3DVolumeDataNode, function (ctx) { x3dom.nodeTypes.ISOSurfaceVolumeData.superClass.call(this, ctx); this.addField_MFNode('renderStyle', x3dom.nodeTypes.X3DVolumeRenderStyleNode); this.addField_SFNode('gradients', x3dom.nodeTypes.X3DTexture3DNode); this.addField_MFFloat(ctx, 'surfaceValues', []); this.addField_SFFloat(ctx, 'contourStepSize', 0); this.addField_SFFloat(ctx, 'surfaceTolerance', 0); }, { nodeChanged: function() {}, fieldChanged: function(fieldName) {} } ) );
/* ### ISOSurfaceVolumeData ### */ x3dom.registerNodeType( "ISOSurfaceVolumeData", "VolumeRendering", defineClass(x3dom.nodeTypes.X3DComposableVolumeRenderStyleNode, function (ctx) { //Some initialization code…
}, {
nodeChanged: function() {}, fieldChanged: function(fieldName) {}, uniforms: function() { //Define the uniforms for the shaders
},
textures: function() { //Prepare the textures
},
vertexShaderText : function() { //Generate vertex shader
},
fragmentShaderText : function (numberOfSlices, slicesOverX, slicesOverY) { //Generate fragment shader
} } ) );
<X3D xmlns='http://www.web3d.org/specifications/x3d-namespace' showStat='false' showLog='true' width='500px' height='500px'> <Scene> <Background skyColor='0.0 0.0 0.0'/> <Viewpoint description='Default' zNear='0.0001' zFar='100'/> <Transform> <VolumeData id='volume' dimensions='4.0 4.0 4.0'> <ImageTextureAtlas containerField='voxels' url='media/volume/aorta4096.png' numberOfSlices='96' slicesOverX='10' slicesOverY='10'/> <ISOSurfaceVolumeData surfaceValues='0.2' contourStepSize='0.2' lightFactor='0.02' opacityFactor='0.4'> <ImageTexture containerField='transferFunction' url='media/volume/transfer/transfer.png'/> </ISOSurfaceVolumeData> </VolumeData> </Transform> </Scene> </X3D>
ISO=20% ISO=40% ISO=60% ISO=80% ISO=80% Lighting OFF
– Integration Combination with mesh models – Implementation of different styles (MIP, X- Ray, Composed …) – Lighting (Phong, Global illumination …) – Animation (4D timesteps, video, flow animation …) – Data transfer optimization (streaming, compression,…) … collaborative visualization
– Web3D Consortium (X3D / MEDX3D) – Fraunhofer IGD (X3DOM) – John Edgar CONGOTE CALLE (main architect and developer of this initiative) ... now Developer Relations Engineer @Unity3D