@@ -2,46 +2,99 @@ import React from 'react';
2
2
import { Component } from 'react' ;
3
3
import {
4
4
View2D ,
5
+ getImageData ,
6
+ loadImageData ,
5
7
vtkInteractorStyleMPRCrosshairs ,
6
8
vtkSVGCrosshairsWidget ,
7
9
} from '@vtk-viewport' ;
8
- import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader ' ;
10
+ import { api as dicomwebClientApi } from 'dicomweb-client ' ;
9
11
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume' ;
10
12
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper' ;
11
13
14
+ const url = 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs' ;
15
+ const studyInstanceUID =
16
+ '1.3.6.1.4.1.14519.5.2.1.2744.7002.373729467545468642229382466905' ;
17
+ const ctSeriesInstanceUID =
18
+ '1.3.6.1.4.1.14519.5.2.1.2744.7002.182837959725425690842769990419' ;
19
+ const searchInstanceOptions = {
20
+ studyInstanceUID,
21
+ } ;
22
+
23
+ function loadDataset ( imageIds , displaySetInstanceUid ) {
24
+ const imageDataObject = getImageData ( imageIds , displaySetInstanceUid ) ;
25
+
26
+ loadImageData ( imageDataObject ) ;
27
+ return imageDataObject ;
28
+ }
29
+
30
+ function createStudyImageIds ( baseUrl , studySearchOptions ) {
31
+ const SOP_INSTANCE_UID = '00080018' ;
32
+ const SERIES_INSTANCE_UID = '0020000E' ;
33
+
34
+ const client = new dicomwebClientApi . DICOMwebClient ( { url } ) ;
35
+
36
+ return new Promise ( ( resolve , reject ) => {
37
+ client . retrieveStudyMetadata ( studySearchOptions ) . then ( instances => {
38
+ const imageIds = instances . map ( metaData => {
39
+ const imageId =
40
+ `wadors:` +
41
+ baseUrl +
42
+ '/studies/' +
43
+ studyInstanceUID +
44
+ '/series/' +
45
+ metaData [ SERIES_INSTANCE_UID ] . Value [ 0 ] +
46
+ '/instances/' +
47
+ metaData [ SOP_INSTANCE_UID ] . Value [ 0 ] +
48
+ '/frames/1' ;
49
+
50
+ cornerstoneWADOImageLoader . wadors . metaDataManager . add (
51
+ imageId ,
52
+ metaData
53
+ ) ;
54
+
55
+ return imageId ;
56
+ } ) ;
57
+
58
+ resolve ( imageIds ) ;
59
+ } , reject ) ;
60
+ } ) ;
61
+ }
62
+
12
63
class VTKCrosshairsExample extends Component {
13
64
state = {
14
65
volumes : [ ] ,
15
66
} ;
16
67
17
- componentDidMount ( ) {
68
+ async componentDidMount ( ) {
18
69
this . apis = [ ] ;
19
70
20
- const reader = vtkHttpDataSetReader . newInstance ( {
21
- fetchGzip : true ,
22
- } ) ;
23
- const volumeActor = vtkVolume . newInstance ( ) ;
24
- const volumeMapper = vtkVolumeMapper . newInstance ( ) ;
71
+ const imageIds = await createStudyImageIds ( url , searchInstanceOptions ) ;
72
+
73
+ let ctImageIds = imageIds . filter ( imageId =>
74
+ imageId . includes ( ctSeriesInstanceUID )
75
+ ) ;
76
+
77
+ const ctImageDataObject = loadDataset ( ctImageIds , 'ctDisplaySet' ) ;
25
78
26
- volumeActor . setMapper ( volumeMapper ) ;
79
+ Promise . all ( ctImageDataObject . insertPixelDataPromises ) . then ( ( ) => {
80
+ const ctImageData = ctImageDataObject . vtkImageData ;
27
81
28
- reader . setUrl ( '/vmhead2-large.vti' , { loadData : true } ) . then ( ( ) => {
29
- const data = reader . getOutputData ( ) ;
30
- const range = data
82
+ const range = ctImageData
31
83
. getPointData ( )
32
84
. getScalars ( )
33
85
. getRange ( ) ;
34
86
35
- const rgbTransferFunction = volumeActor
36
- . getProperty ( )
37
- . getRGBTransferFunction ( 0 ) ;
87
+ const mapper = vtkVolumeMapper . newInstance ( ) ;
88
+ const ctVol = vtkVolume . newInstance ( ) ;
89
+ const rgbTransferFunction = ctVol . getProperty ( ) . getRGBTransferFunction ( 0 ) ;
38
90
91
+ mapper . setInputData ( ctImageData ) ;
92
+ mapper . setMaximumSamplesPerRay ( 2000 ) ;
39
93
rgbTransferFunction . setRange ( range [ 0 ] , range [ 1 ] ) ;
40
-
41
- volumeMapper . setInputData ( data ) ;
94
+ ctVol . setMapper ( mapper ) ;
42
95
43
96
this . setState ( {
44
- volumes : [ volumeActor ] ,
97
+ volumes : [ ctVol ] ,
45
98
} ) ;
46
99
} ) ;
47
100
}
@@ -53,22 +106,45 @@ class VTKCrosshairsExample extends Component {
53
106
const apis = this . apis ;
54
107
const renderWindow = api . genericRenderWindow . getRenderWindow ( ) ;
55
108
109
+ // Add svg widget
56
110
api . addSVGWidget (
57
111
vtkSVGCrosshairsWidget . newInstance ( ) ,
58
112
'crosshairsWidget'
59
113
) ;
60
114
61
115
const istyle = vtkInteractorStyleMPRCrosshairs . newInstance ( ) ;
62
116
117
+ // add istyle
63
118
api . setInteractorStyle ( {
64
119
istyle,
65
120
configuration : { apis, apiIndex : viewportIndex } ,
66
121
} ) ;
67
122
123
+ // set blend mode to MIP.
124
+ const mapper = api . volumes [ 0 ] . getMapper ( ) ;
125
+ if ( mapper . setBlendModeToMaximumIntensity ) {
126
+ mapper . setBlendModeToMaximumIntensity ( ) ;
127
+ }
128
+
129
+ api . setSlabThickness ( 0.1 ) ;
130
+
68
131
renderWindow . render ( ) ;
69
132
} ;
70
133
} ;
71
134
135
+ handleSlabThicknessChange ( evt ) {
136
+ const value = evt . target . value ;
137
+ const valueInMM = value / 10 ;
138
+ const apis = this . apis ;
139
+
140
+ apis . forEach ( api => {
141
+ const renderWindow = api . genericRenderWindow . getRenderWindow ( ) ;
142
+
143
+ api . setSlabThickness ( valueInMM ) ;
144
+ renderWindow . render ( ) ;
145
+ } ) ;
146
+ }
147
+
72
148
render ( ) {
73
149
if ( ! this . state . volumes || ! this . state . volumes . length ) {
74
150
return < h4 > Loading...</ h4 > ;
@@ -77,34 +153,40 @@ class VTKCrosshairsExample extends Component {
77
153
return (
78
154
< >
79
155
< div className = "row" >
80
- < div className = "col-xs-6 " >
156
+ < div className = "col-xs-4 " >
81
157
< p >
82
158
This example demonstrates how to use the Crosshairs manipulator.
83
159
</ p >
160
+ < label htmlFor = "set-slab-thickness" > SlabThickness: </ label >
161
+ < input
162
+ id = "set-slab-thickness"
163
+ type = "range"
164
+ name = "points"
165
+ min = "1"
166
+ max = "5000"
167
+ onChange = { this . handleSlabThicknessChange . bind ( this ) }
168
+ />
84
169
</ div >
85
170
</ div >
86
171
< div className = "row" >
87
- < div className = "col-xs-6 col-xs-6 " >
172
+ < div className = "col-sm-4 " >
88
173
< View2D
89
174
volumes = { this . state . volumes }
90
- onCreated = { this . storeApi ( 2 ) }
175
+ onCreated = { this . storeApi ( 0 ) }
91
176
orientation = { { sliceNormal : [ 0 , 1 , 0 ] , viewUp : [ 0 , 0 , 1 ] } }
92
177
/>
93
178
</ div >
94
- < div className = "col-xs-6 col-xs-6 " >
179
+ < div className = "col-sm-4 " >
95
180
< View2D
96
181
volumes = { this . state . volumes }
97
182
onCreated = { this . storeApi ( 1 ) }
98
183
orientation = { { sliceNormal : [ 1 , 0 , 0 ] , viewUp : [ 0 , 0 , 1 ] } }
99
184
/>
100
185
</ div >
101
- </ div >
102
-
103
- < div className = "row" >
104
- < div className = "col-xs-6 col-xs-6" >
186
+ < div className = "col-sm-4" >
105
187
< View2D
106
188
volumes = { this . state . volumes }
107
- onCreated = { this . storeApi ( 0 ) }
189
+ onCreated = { this . storeApi ( 2 ) }
108
190
orientation = { { sliceNormal : [ 0 , 0 , 1 ] , viewUp : [ 0 , - 1 , 0 ] } }
109
191
/>
110
192
</ div >
0 commit comments