volume.py
27.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
#--------------------------------------------------------------------------
# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas
# Copyright: (C) 2001 Centro de Pesquisas Renato Archer
# Homepage: http://www.softwarepublico.gov.br
# Contact: invesalius@cti.gov.br
# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt)
#--------------------------------------------------------------------------
# Este programa e software livre; voce pode redistribui-lo e/ou
# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme
# publicada pela Free Software Foundation; de acordo com a versao 2
# da Licenca.
#
# Este programa eh distribuido na expectativa de ser util, mas SEM
# QUALQUER GARANTIA; sem mesmo a garantia implicita de
# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM
# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
# detalhes.
#--------------------------------------------------------------------------
import plistlib
import os
import weakref
import numpy
import vtk
import wx
from wx.lib.pubsub import pub as Publisher
import invesalius.constants as const
import invesalius.project as prj
import invesalius.data.slice_ as slice_
import invesalius.data.converters as converters
import invesalius.data.vtk_utils as vtk_utils
from vtk.util import numpy_support
import invesalius.session as ses
Kernels = {
"Basic Smooth 5x5" : [1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 4.0, 4.0, 4.0, 1.0,
1.0, 4.0, 12.0, 4.0, 1.0,
1.0, 4.0, 4.0, 4.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0]
}
SHADING = {
"Default": {
"ambient" :0.15,
"diffuse" :0.9,
"specular" :0.3,
"specularPower" :15,
},
"Glossy Vascular":{
"ambient" :0.15,
"diffuse" :0.28,
"specular" :1.42,
"specularPower" :50,
},
"Glossy Bone": {
"ambient" :0.15,
"diffuse" :0.24,
"specular" :1.17,
"specularPower" :6.98,
},
"Endoscopy": {
"ambient" :0.12,
"diffuse" :0.64,
"specular" :0.73,
"specularPower" :50,
}
}
class Volume():
def __init__(self):
self.config = None
self.exist = None
self.color_transfer = None
self.opacity_transfer_func = None
self.ww = None
self.wl = None
self.curve = 0
self.plane = None
self.plane_on = False
self.volume = None
self.image = None
self.loaded_image = 0
self.to_reload = False
self.__bind_events()
def __bind_events(self):
Publisher.subscribe(self.OnHideVolume,
'Hide raycasting volume')
Publisher.subscribe(self.OnUpdatePreset,
'Update raycasting preset')
Publisher.subscribe(self.OnSetCurve,
'Set raycasting curve')
Publisher.subscribe(self.OnSetWindowLevel,
'Set raycasting wwwl')
Publisher.subscribe(self.Refresh,
'Set raycasting refresh')
Publisher.subscribe(self.OnSetRelativeWindowLevel,
'Set raycasting relative window and level')
Publisher.subscribe(self.OnEnableTool,
'Enable raycasting tool')
Publisher.subscribe(self.OnCloseProject, 'Close project data')
Publisher.subscribe(self.ChangeBackgroundColour,
'Change volume viewer background colour')
Publisher.subscribe(self.ResetRayCasting, 'Reset Reaycasting')
Publisher.subscribe(self.OnFlipVolume, 'Flip volume')
def ResetRayCasting(self, pub_evt):
if self.exist:
self.exist = None
self.LoadVolume()
def OnCloseProject(self, pubsub_evt):
self.CloseProject()
def CloseProject(self):
#if self.plane:
# self.plane = None
# Publisher.sendMessage('Remove surface actor from viewer', self.plane_actor)
if self.plane:
self.plane.DestroyObjs()
del self.plane
self.plane = 0
if self.exist:
self.exist = None
Publisher.sendMessage('Remove surface actor from viewer', self.volume)
Publisher.sendMessage('Disable volume cut menu')
def OnLoadVolume(self, pubsub_evt):
label = pubsub_evt.data
#self.LoadConfig(label)
self.LoadVolume()
def OnHideVolume(self, pubsub_evt):
self.volume.SetVisibility(0)
if (self.plane and self.plane_on):
self.plane.Disable()
Publisher.sendMessage('Render volume viewer')
def OnShowVolume(self, pubsub_evt = None):
if self.exist:
self.volume.SetVisibility(1)
if (self.plane and self.plane_on):
self.plane.Enable()
Publisher.sendMessage('Render volume viewer')
else:
Publisher.sendMessage('Load raycasting preset', const.RAYCASTING_LABEL)
self.LoadConfig()
self.LoadVolume()
self.exist = 1
def OnUpdatePreset(self, pubsub_evt):
self.__load_preset_config()
if self.config:
if self.to_reload:
self.exist = False
Publisher.sendMessage('Unload volume', self.volume)
if self.exist:
self.__load_preset()
self.volume.SetVisibility(1)
#Publisher.sendMessage('Render volume viewer')
else:
self.LoadVolume()
self.CalculateHistogram()
self.exist = 1
colour = self.GetBackgroundColour()
Publisher.sendMessage('Change volume viewer background colour', colour)
Publisher.sendMessage('Change volume viewer gui colour', colour)
else:
Publisher.sendMessage('Unload volume', self.volume)
del self.image
del self.imagedata
del self.final_imagedata
del self.volume
del self.color_transfer
del self.opacity_transfer_func
del self.volume_properties
del self.volume_mapper
self.volume = None
self.exist = False
self.loaded_image = False
self.image = None
self.final_imagedata = None
self.opacity_transfer_func = None
self.color_transfer = None
Publisher.sendMessage('Render volume viewer')
def OnFlipVolume(self, pubsub_evt):
print "Flipping Volume"
self.loaded_image = False
del self.image
self.image = None
self.to_reload = True
def __load_preset_config(self):
self.config = prj.Project().raycasting_preset
def __update_colour_table(self):
if self.config['advancedCLUT']:
self.Create16bColorTable(self.scale)
self.CreateOpacityTable(self.scale)
else:
self.Create8bColorTable(self.scale)
self.Create8bOpacityTable(self.scale)
def __load_preset(self):
# Update colour table
self.__update_colour_table()
# Update convolution filter
original_imagedata = self.imagedata.GetOutput()
imagedata = self.ApplyConvolution(original_imagedata)
self.volume_mapper.SetInputData(imagedata)
# Update other information
self.SetShading()
self.SetTypeRaycasting()
def OnSetCurve(self, pubsub_evt):
self.curve = pubsub_evt.data
self.CalculateWWWL()
ww = self.ww
wl = self.wl
Publisher.sendMessage('Set volume window and level text',
(ww, wl))
def OnSetRelativeWindowLevel(self, pubsub_evt):
diff_wl, diff_ww = pubsub_evt.data
ww = self.ww + diff_ww
wl = self.wl + diff_wl
Publisher.sendMessage('Set volume window and level text',
(ww, wl))
self.SetWWWL(ww, wl)
self.ww = ww
self.wl = wl
def OnSetWindowLevel(self, pubsub_evt):
ww, wl, n = pubsub_evt.data
self.curve = n
self.SetWWWL(ww,wl)
def SetWWWL(self, ww, wl):
if self.config['advancedCLUT']:
try:
curve = self.config['16bitClutCurves'][self.curve]
except IndexError:
self.curve = 0
curve = self.config['16bitClutCurves'][self.curve]
p1 = curve[0]
p2 = curve[-1]
half = (p2['x'] - p1['x']) / 2.0
middle = p1['x'] + half
shiftWL = wl - middle
shiftWW = p1['x'] + shiftWL - (wl - 0.5 * ww)
factor = 1.0
for n,i in enumerate(curve):
factor = abs(i['x'] - middle) / half
if factor < 0:
factor = 0
i['x'] += shiftWL
if n < len(curve)/2.0:
i['x'] -= shiftWW * factor
else:
i['x'] += shiftWW * factor
else:
self.config['wl'] = wl
self.config['ww'] = ww
self.__update_colour_table()
def CalculateWWWL(self):
"""
Get the window width & level from the selected curve
"""
try:
curve = self.config['16bitClutCurves'][self.curve]
except IndexError:
self.curve -= 1
curve = self.config['16bitClutCurves'][self.curve]
first_point = curve[0]['x']
last_point = curve[-1]['x']
self.ww = last_point - first_point
self.wl = first_point + self.ww / 2.0
def Refresh(self, pubsub_evt):
self.__update_colour_table()
def Create16bColorTable(self, scale):
if self.color_transfer:
color_transfer = self.color_transfer
else:
color_transfer = vtk.vtkColorTransferFunction()
color_transfer.RemoveAllPoints()
curve_table = self.config['16bitClutCurves']
color_table = self.config['16bitClutColors']
colors = []
for i, l in enumerate(curve_table):
for j, lopacity in enumerate(l):
gray_level = lopacity['x']
r = color_table[i][j]['red']
g = color_table[i][j]['green']
b = color_table[i][j]['blue']
colors.append((gray_level, r, g, b))
color_transfer.AddRGBPoint(
self.TranslateScale(scale, gray_level),
r, g, b)
self.color_transfer = color_transfer
def Create8bColorTable(self, scale):
if self.color_transfer:
color_transfer = self.color_transfer
else:
color_transfer = vtk.vtkColorTransferFunction()
color_transfer.RemoveAllPoints()
color_preset = self.config['CLUT']
if color_preset != "No CLUT":
p = plistlib.readPlist(
os.path.join(const.RAYCASTING_PRESETS_DIRECTORY,
'color_list', color_preset + '.plist'))
r = p['Red']
g = p['Green']
b = p['Blue']
colors = zip(r,g,b)
else:
# Grayscale from black to white
colors = [(i, i, i) for i in xrange(256)]
ww = self.config['ww']
wl = self.TranslateScale(scale, self.config['wl'])
init = wl - ww/2.0
inc = ww / (len(colors) - 1.0)
for n,rgb in enumerate(colors):
color_transfer.AddRGBPoint(init + n * inc, *[i/255.0 for i in rgb])
self.color_transfer = color_transfer
def CreateOpacityTable(self, scale):
if self.opacity_transfer_func:
opacity_transfer_func = self.opacity_transfer_func
else:
opacity_transfer_func = vtk.vtkPiecewiseFunction()
opacity_transfer_func.RemoveAllPoints()
curve_table = self.config['16bitClutCurves']
opacities = []
ww = self.config['ww']
wl = self.config['wl']
self.ww = ww
self.wl = wl
l1 = wl - ww/2.0
l2 = wl + ww/2.0
k1 = 0.0
k2 = 1.0
opacity_transfer_func.AddSegment(0, 0, 2**16-1, 0)
for i, l in enumerate(curve_table):
for j, lopacity in enumerate(l):
gray_level = lopacity['x']
#if gray_level <= l1:
# opacity = k1
#elif gray_level > l2:
# opacity = k2
#else:
opacity = lopacity['y']
opacities.append((gray_level, opacity))
opacity_transfer_func.AddPoint(
self.TranslateScale(scale, gray_level), opacity)
self.opacity_transfer_func = opacity_transfer_func
def Create8bOpacityTable(self, scale):
if self.opacity_transfer_func:
opacity_transfer_func = self.opacity_transfer_func
else:
opacity_transfer_func = vtk.vtkPiecewiseFunction()
opacity_transfer_func.RemoveAllPoints()
opacities = []
ww = self.config['ww']
wl = self.TranslateScale(scale, self.config['wl'])
l1 = wl - ww/2.0
l2 = wl + ww/2.0
self.ww = ww
self.wl = self.config['wl']
opacity_transfer_func.RemoveAllPoints()
opacity_transfer_func.AddSegment(0, 0, 2**16-1, 0)
k1 = 0.0
k2 = 1.0
opacity_transfer_func.AddPoint(l1, 0)
opacity_transfer_func.AddPoint(l2, 1)
self.opacity_transfer_func = opacity_transfer_func
return opacity_transfer_func
def GetBackgroundColour(self):
colour = (self.config['backgroundColorRedComponent'],
self.config['backgroundColorGreenComponent'],
self.config['backgroundColorBlueComponent'])
return colour
def ChangeBackgroundColour(self, pubsub_evt):
if (self.config):
self.config['backgroundColorRedComponent'] = pubsub_evt.data[0] * 255
self.config['backgroundColorGreenComponent'] = pubsub_evt.data[1] * 255
self.config['backgroundColorBlueComponent'] = pubsub_evt.data[2] * 255
def BuildTable():
curve_table = p['16bitClutCurves']
color_background = (p['backgroundColorRedComponent'],
p['backgroundColorGreenComponent'],
p['backgroundColorBlueComponent'])
color_background = [i for i in color_background]
opacities = []
colors = []
for i, l in enumerate(curve_table):
for j, lopacity in enumerate(l):
gray_level = lopacity['x']
opacity = lopacity['y']
opacities.append((gray_level, opacity))
r = color_table[i][j]['red']
g = color_table[i][j]['green']
b = color_table[i][j]['blue']
colors.append((gray_level, r, g, b))
return colors, opacities, color_background, p['useShading']
def SetShading(self):
if self.config['useShading']:
self.volume_properties.ShadeOn()
else:
self.volume_properties.ShadeOff()
shading = SHADING[self.config['shading']]
self.volume_properties.SetAmbient(shading['ambient'])
self.volume_properties.SetDiffuse(shading['diffuse'])
self.volume_properties.SetSpecular(shading['specular'])
self.volume_properties.SetSpecularPower(shading['specularPower'])
def SetTypeRaycasting(self):
if self.volume_mapper.IsA("vtkFixedPointVolumeRayCastMapper") or self.volume_mapper.IsA("vtkGPUVolumeRayCastMapper"):
if self.config.get('MIP', False):
self.volume_mapper.SetBlendModeToMaximumIntensity()
else:
self.volume_mapper.SetBlendModeToComposite()
else:
if self.config.get('MIP', False):
raycasting_function = vtk.vtkVolumeRayCastMIPFunction()
else:
raycasting_function = vtk.vtkVolumeRayCastCompositeFunction()
raycasting_function.SetCompositeMethodToInterpolateFirst()
if ses.Session().rendering == '0':
self.volume_mapper.SetVolumeRayCastFunction(raycasting_function)
def ApplyConvolution(self, imagedata, update_progress = None):
number_filters = len(self.config['convolutionFilters'])
if number_filters:
if not(update_progress):
update_progress = vtk_utils.ShowProgress(number_filters)
for filter in self.config['convolutionFilters']:
convolve = vtk.vtkImageConvolve()
convolve.SetInputData(imagedata)
convolve.SetKernel5x5([i/60.0 for i in Kernels[filter]])
# convolve.ReleaseDataFlagOn()
convolve_ref = weakref.ref(convolve)
convolve_ref().AddObserver("ProgressEvent", lambda obj,evt:
update_progress(convolve_ref(), "Rendering..."))
convolve.Update()
del imagedata
imagedata = convolve.GetOutput()
del convolve
#convolve.GetOutput().ReleaseDataFlagOn()
return imagedata
def LoadImage(self):
slice_data = slice_.Slice()
n_array = slice_data.matrix
spacing = slice_data.spacing
slice_number = 0
orientation = 'AXIAL'
image = converters.to_vtk(n_array, spacing, slice_number, orientation)
self.image = image
def LoadVolume(self):
proj = prj.Project()
#image = imagedata_utils.to_vtk(n_array, spacing, slice_number, orientation)
if not self.loaded_image:
self.LoadImage()
self.loaded_image = 1
image = self.image
number_filters = len(self.config['convolutionFilters'])
if (prj.Project().original_orientation == const.AXIAL):
flip_image = True
else:
flip_image = False
#if (flip_image):
update_progress= vtk_utils.ShowProgress(2 + number_filters)
# Flip original vtkImageData
flip = vtk.vtkImageFlip()
flip.SetInputData(image)
flip.SetFilteredAxis(1)
flip.FlipAboutOriginOn()
# flip.ReleaseDataFlagOn()
flip_ref = weakref.ref(flip)
flip_ref().AddObserver("ProgressEvent", lambda obj,evt:
update_progress(flip_ref(), "Rendering..."))
flip.Update()
image = flip.GetOutput()
scale = image.GetScalarRange()
self.scale = scale
cast = vtk.vtkImageShiftScale()
cast.SetInputData(image)
cast.SetShift(abs(scale[0]))
cast.SetOutputScalarTypeToUnsignedShort()
# cast.ReleaseDataFlagOn()
cast_ref = weakref.ref(cast)
cast_ref().AddObserver("ProgressEvent", lambda obj,evt:
update_progress(cast_ref(), "Rendering..."))
cast.Update()
image2 = cast
self.imagedata = image2
if self.config['advancedCLUT']:
self.Create16bColorTable(scale)
self.CreateOpacityTable(scale)
else:
self.Create8bColorTable(scale)
self.Create8bOpacityTable(scale)
image2 = self.ApplyConvolution(image2.GetOutput(), update_progress)
self.final_imagedata = image2
# Changed the vtkVolumeRayCast to vtkFixedPointVolumeRayCastMapper
# because it's faster and the image is better
# TODO: To test if it's true.
if const.TYPE_RAYCASTING_MAPPER:
volume_mapper = vtk.vtkVolumeRayCastMapper()
#volume_mapper.AutoAdjustSampleDistancesOff()
#volume_mapper.SetInput(image2)
#volume_mapper.SetVolumeRayCastFunction(composite_function)
#volume_mapper.SetGradientEstimator(gradientEstimator)
volume_mapper.IntermixIntersectingGeometryOn()
self.volume_mapper = volume_mapper
else:
if int(ses.Session().rendering) == 0:
volume_mapper = vtk.vtkFixedPointVolumeRayCastMapper()
#volume_mapper.AutoAdjustSampleDistancesOff()
self.volume_mapper = volume_mapper
volume_mapper.IntermixIntersectingGeometryOn()
else:
volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
self.volume_mapper = volume_mapper
self.SetTypeRaycasting()
volume_mapper.SetInputData(image2)
# TODO: Look to this
#volume_mapper_hw = vtk.vtkVolumeTextureMapper3D()
#volume_mapper_hw.SetInput(image2)
#Cut Plane
#CutPlane(image2, volume_mapper)
#self.color_transfer = color_transfer
volume_properties = vtk.vtkVolumeProperty()
#volume_properties.IndependentComponentsOn()
volume_properties.SetInterpolationTypeToLinear()
volume_properties.SetColor(self.color_transfer)
try:
volume_properties.SetScalarOpacity(self.opacity_transfer_func)
except NameError:
pass
# Using these lines to improve the raycasting quality. These values
# seems related to the distance from ray from raycasting.
# TODO: Need to see values that improve the quality and don't decrease
# the performance. 2.0 seems to be a good value to pix_diag
pix_diag = 2.0
volume_mapper.SetImageSampleDistance(0.25)
volume_mapper.SetSampleDistance(pix_diag / 5.0)
volume_properties.SetScalarOpacityUnitDistance(pix_diag)
self.volume_properties = volume_properties
self.SetShading()
volume = vtk.vtkVolume()
volume.SetMapper(volume_mapper)
volume.SetProperty(volume_properties)
self.volume = volume
colour = self.GetBackgroundColour()
self.exist = 1
Publisher.sendMessage('Load volume into viewer',
(volume, colour, (self.ww, self.wl)))
del flip
del cast
def OnEnableTool(self, pubsub_evt):
tool_name, enable = pubsub_evt.data
if tool_name == _("Cut plane"):
if self.plane:
if enable:
self.plane_on = True
self.plane.Enable()
else:
self.plane_on = False
self.plane.Disable()
else:
# self.final_imagedata.Update()
self.plane_on = True
self.plane = CutPlane(self.final_imagedata,
self.volume_mapper)
def CalculateHistogram(self):
image = self.image
r = int(image.GetScalarRange()[1] - image.GetScalarRange()[0])
accumulate = vtk.vtkImageAccumulate()
accumulate.SetInputData(image)
accumulate.SetComponentExtent(0, r -1, 0, 0, 0, 0)
accumulate.SetComponentOrigin(image.GetScalarRange()[0], 0, 0)
# accumulate.ReleaseDataFlagOn()
accumulate.Update()
n_image = numpy_support.vtk_to_numpy(accumulate.GetOutput().GetPointData().GetScalars())
del accumulate
Publisher.sendMessage('Load histogram', (n_image,
image.GetScalarRange()))
def TranslateScale(self, scale, value):
#if value < 0:
# valor = 2**16 - abs(value)
#else:
# valor = value
return value - scale[0]
class CutPlane:
def __init__(self, img, volume_mapper):
self.img = img
self.volume_mapper = volume_mapper
self.Create()
self.__bind_events()
def __bind_events(self):
Publisher.subscribe(self.Reset,
'Reset Cut Plane')
Publisher.subscribe(self.Enable,
'Enable Cut Plane')
Publisher.subscribe(self.Disable,
'Disable Cut Plane')
def Create(self):
self.plane_widget = plane_widget = vtk.vtkImagePlaneWidget()
plane_widget.SetInputData(self.img)
plane_widget.SetPlaneOrientationToXAxes()
#plane_widget.SetResliceInterpolateToLinear()
plane_widget.TextureVisibilityOff()
#Set left mouse button to move and rotate plane
plane_widget.SetLeftButtonAction(1)
#SetColor margin to green
margin_property = plane_widget.GetMarginProperty()
margin_property.SetColor(0,0.8,0)
#Disable cross
cursor_property = plane_widget.GetCursorProperty()
cursor_property.SetOpacity(0)
self.plane_source = plane_source = vtk.vtkPlaneSource()
plane_source.SetOrigin(plane_widget.GetOrigin())
plane_source.SetPoint1(plane_widget.GetPoint1())
plane_source.SetPoint2(plane_widget.GetPoint2())
plane_source.SetNormal(plane_widget.GetNormal())
plane_mapper = self.plane_mapper = vtk.vtkPolyDataMapper()
plane_mapper.SetInputData(plane_source.GetOutput())
self.plane_actor = plane_actor = vtk.vtkActor()
plane_actor.SetMapper(plane_mapper)
plane_actor.GetProperty().BackfaceCullingOn()
plane_actor.GetProperty().SetOpacity(0)
plane_widget.AddObserver("InteractionEvent", self.Update)
Publisher.sendMessage('AppendActor', self.plane_actor)
Publisher.sendMessage('Set Widget Interactor', self.plane_widget)
plane_actor.SetVisibility(1)
plane_widget.On()
self.plane = plane = vtk.vtkPlane()
plane.SetNormal(self.plane_source.GetNormal())
plane.SetOrigin(self.plane_source.GetOrigin())
self.volume_mapper.AddClippingPlane(plane)
#Storage First Position
self.origin = plane_widget.GetOrigin()
self.p1 = plane_widget.GetPoint1()
self.p2 = plane_widget.GetPoint2()
self.normal = plane_widget.GetNormal()
def Update(self, a, b):
plane_source = self.plane_source
plane_widget = self.plane_widget
plane_source.SetOrigin(plane_widget.GetOrigin())
plane_source.SetPoint1(plane_widget.GetPoint1())
plane_source.SetPoint2(plane_widget.GetPoint2())
plane_source.SetNormal(plane_widget.GetNormal())
self.plane_actor.VisibilityOn()
self.plane.SetNormal(plane_source.GetNormal())
self.plane.SetOrigin(plane_source.GetOrigin())
Publisher.sendMessage('Render volume viewer', None)
def Enable(self, evt_pubsub=None):
self.plane_widget.On()
self.plane_actor.VisibilityOn()
self.volume_mapper.AddClippingPlane(self.plane)
Publisher.sendMessage('Render volume viewer', None)
def Disable(self,evt_pubsub=None):
self.plane_widget.Off()
self.plane_actor.VisibilityOff()
self.volume_mapper.RemoveClippingPlane(self.plane)
Publisher.sendMessage('Render volume viewer', None)
def Reset(self, evt_pubsub=None):
plane_source = self.plane_source
plane_widget = self.plane_widget
plane_source.SetOrigin(self.origin)
plane_source.SetPoint1(self.p1)
plane_source.SetPoint2(self.p2)
plane_source.SetNormal(self.normal)
self.plane_actor.VisibilityOn()
self.plane.SetNormal(self.normal)
self.plane.SetOrigin(self.origin)
Publisher.sendMessage('Render volume viewer', None)
def DestroyObjs(self):
Publisher.sendMessage('Remove surface actor from viewer', self.plane_actor)
self.Disable()
del self.plane_widget
del self.plane_source
del self.plane_actor
del self.normal
del self.plane