Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
fslpy
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Analyze
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
FSL
fslpy
Commits
4759dd32
Commit
4759dd32
authored
10 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
GLImageData stuff separated from SliceCanvas.
parent
8e9fbb99
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
fsl/fslview/glimagedata.py
+296
-0
296 additions, 0 deletions
fsl/fslview/glimagedata.py
fsl/fslview/slicecanvas.py
+20
-286
20 additions, 286 deletions
fsl/fslview/slicecanvas.py
with
316 additions
and
286 deletions
fsl/fslview/glimagedata.py
0 → 100644
+
296
−
0
View file @
4759dd32
#!/usr/bin/env python
#
# glimagedata.py - Create OpenGL data to render 2D slices of a 3D image.
# A GLImageData object encapsulates the OpenGL information necessary
# to render 2D slices of a 3D image.
#
# A slice from one image is rendered using four buffers and two textures.
# The first buffer, the 'geometry buffer' simply contains the 3D
# coordinates (single precision floating point) of four vertices, which
# define the geometry of a single voxel (using triangle strips).
# The remaining buffers contain the X, Y, and Z coordinates of the voxels
# in the slice to be displayed. These coordinates are stored as single
# precision floating points, and used both to position a voxel, and to
# look up its value in the 3D data texture (see below).
# The image data itself is stored as a 3D texture, with each voxel value
# stored as a single unsigned byte in the range 0-255.
# Finally, a 1D texture is used is used to store a lookup table containing
# an RGBA8 colour map, to colour each voxel according to its value.
#
# All of these things are created when a GLImageData object is
# instantiated. They are available as attributes of the object:
#
# - dataBuffer
# - xBuffer
# - yBuffer
# - zBuffer
# - geomBuffer
# - colourBuffer
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import
numpy
as
np
import
OpenGL.GL
as
gl
import
OpenGL.arrays.vbo
as
vbo
# This extension provides the GL_R8 constant,
# which is built into modern versions of OpenGL.
import
OpenGL.GL.ARB.texture_rg
as
arbrg
class
GLImageData
(
object
):
def
__init__
(
self
,
image
,
xax
,
yax
,
imageDisplay
=
None
):
"""
Initialise the OpenGL data buffers required to render the given image.
Parameters:
- image: A fsl.data.fslimage.Image object.
- xax: The image axis which maps to the screen x axis.
- yax: The image axis which maps to the screen y axis.
- imageDisplay: Optional. A fsl.data.fslimage.ImageDisplay object
which describes how the image is to be displayed.
If not provided, the default image.display instance
is used (see fsl.data.fslimage.ImageDisplay for
details).
"""
self
.
image
=
image
self
.
xax
=
xax
self
.
yax
=
yax
if
imageDisplay
is
not
None
:
self
.
display
=
imageDisplay
else
:
self
.
display
=
image
.
display
# Maximum number of colours used to draw image data.
# Keep this to a power of two, as some GL implementations
# will complain/misbehave if it isn't.
self
.
colourResolution
=
256
self
.
initGLImageData
()
def
initGLImageData
(
self
):
"""
Creates and initialises the OpenGL data for the fslimage.Image
object that was passed to the GLImageData constructor.
"""
image
=
self
.
image
xax
=
self
.
xax
yax
=
self
.
yax
# The geometry buffer defines the geometry of
# a single voxel, rendered as a triangle strip.
geomData
=
np
.
zeros
((
4
,
3
),
dtype
=
np
.
float32
)
geomData
[:,
[
xax
,
yax
]]
=
[[
-
0.5
,
-
0.5
],
[
0.5
,
-
0.5
],
[
-
0.5
,
0.5
],
[
0.5
,
0.5
]]
geomData
=
geomData
.
ravel
(
'
C
'
)
geomBuffer
=
vbo
.
VBO
(
geomData
,
gl
.
GL_STATIC_DRAW
)
# x/y/z coordinates are stored as VBO arrays
voxData
=
[]
for
dim
in
image
.
shape
:
data
=
np
.
arange
(
0
,
dim
,
dtype
=
np
.
float32
)
voxData
.
append
(
data
)
# the screen x coordinate data has to be repeated (ydim)
# times - we are drawing row-wise, and opengl does not
# allow us to loop over a VBO in a single instance
# rendering call
voxData
[
xax
]
=
np
.
tile
(
voxData
[
xax
],
image
.
shape
[
yax
])
xBuffer
=
vbo
.
VBO
(
voxData
[
0
],
gl
.
GL_STATIC_DRAW
)
yBuffer
=
vbo
.
VBO
(
voxData
[
1
],
gl
.
GL_STATIC_DRAW
)
zBuffer
=
vbo
.
VBO
(
voxData
[
2
],
gl
.
GL_STATIC_DRAW
)
# The colour buffer, containing a map of
# colours (stored on the GPU as a 1D texture)
# This is initialised in the updateColourBuffer
# method
colourBuffer
=
gl
.
glGenTextures
(
1
)
self
.
dataBuffer
=
self
.
initImageBuffer
()
self
.
voxXBuffer
=
xBuffer
self
.
voxYBuffer
=
yBuffer
self
.
voxZBuffer
=
zBuffer
self
.
geomBuffer
=
geomBuffer
self
.
colourBuffer
=
colourBuffer
# Add listeners to this image so the view can be
# updated when its display properties are changed
self
.
configDisplayListeners
()
# Create the colour buffer for the given image
self
.
updateColourBuffer
()
def
initImageBuffer
(
self
):
"""
Initialises the OpenGL buffer used to store the data for the given
image. The buffer is stored as an attribute of the image and, if it
has already been created (e.g. by another SliceCanvas object), the
existing buffer is returned.
"""
image
=
self
.
image
texShape
=
2
**
(
np
.
ceil
(
np
.
log2
(
image
.
shape
)))
pad
=
[(
0
,
l
-
s
)
for
(
l
,
s
)
in
zip
(
texShape
,
image
.
shape
)]
self
.
imageTexShape
=
texShape
try
:
imageBuffer
=
image
.
getAttribute
(
'
glImageBuffer
'
)
except
:
imageBuffer
=
None
if
imageBuffer
is
not
None
:
return
imageBuffer
# The image data is normalised to lie
# between 0 and 255, and cast to uint8
imageData
=
np
.
array
(
image
.
data
,
dtype
=
np
.
float32
)
imageData
=
255.0
*
(
imageData
-
imageData
.
min
())
/
\
(
imageData
.
max
()
-
imageData
.
min
())
# and each dimension is padded so it has a
# power-of-two length. Ugh. This is a horrible,
# but as far as I'm aware, necessary hack. At
# least it's necessary using the OpenGL 2.1
# API on OSX mavericks. It massively increases
# image load time, too.
imageData
=
np
.
pad
(
imageData
,
pad
,
'
constant
'
,
constant_values
=
0
)
imageData
=
np
.
array
(
imageData
,
dtype
=
np
.
uint8
)
# Then flattened, with fortran dimension ordering,
# so the data, as stored on the GPU, has its first
# dimension as the fastest changing.
imageData
=
imageData
.
ravel
(
order
=
'
F
'
)
# Image data is stored on the GPU as a 3D texture
imageBuffer
=
gl
.
glGenTextures
(
1
)
gl
.
glBindTexture
(
gl
.
GL_TEXTURE_3D
,
imageBuffer
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_MAG_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_MIN_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_S
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_T
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_R
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexImage3D
(
gl
.
GL_TEXTURE_3D
,
0
,
arbrg
.
GL_R8
,
texShape
[
0
],
texShape
[
1
],
texShape
[
2
],
0
,
gl
.
GL_RED
,
gl
.
GL_UNSIGNED_BYTE
,
imageData
)
# And added as an attribute of the image, so
# other things which want to render the image
# don't need to duplicate all of that data.
image
.
setAttribute
(
'
glImageBuffer
'
,
imageBuffer
)
return
imageBuffer
def
updateColourBuffer
(
self
):
"""
Regenerates the colour buffer used to colour image voxels.
"""
display
=
self
.
display
colourBuffer
=
self
.
colourBuffer
# Here we are creating a range of values to be passed
# to the matplotlib.colors.Colormap instance of the
# image display. We scale this range such that data
# values which lie outside the configured display range
# will map to values below 0.0 or above 1.0. It is
# assumed that the Colormap instance is configured to
# generate appropriate colours for these out-of-range
# values.
normalRange
=
np
.
linspace
(
0.0
,
1.0
,
self
.
colourResolution
)
normalStep
=
1.0
/
(
self
.
colourResolution
-
1
)
normMin
=
(
display
.
displayMin
-
display
.
dataMin
)
/
\
(
display
.
dataMax
-
display
.
dataMin
)
normMax
=
(
display
.
displayMax
-
display
.
dataMin
)
/
\
(
display
.
dataMax
-
display
.
dataMin
)
newStep
=
normalStep
/
(
normMax
-
normMin
)
newRange
=
(
normalRange
-
normMin
)
*
(
newStep
/
normalStep
)
# Create [self.colourResolution] rgb values,
# spanning the entire range of the image
# colour map
colourmap
=
display
.
cmap
(
newRange
)
# The colour data is stored on
# the GPU as 8 bit rgba tuples
colourmap
=
np
.
floor
(
colourmap
*
255
)
colourmap
=
np
.
array
(
colourmap
,
dtype
=
np
.
uint8
)
colourmap
=
colourmap
.
ravel
(
order
=
'
C
'
)
# GL texture creation stuff
gl
.
glBindTexture
(
gl
.
GL_TEXTURE_1D
,
colourBuffer
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_MAG_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_MIN_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_WRAP_S
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexImage1D
(
gl
.
GL_TEXTURE_1D
,
0
,
gl
.
GL_RGBA8
,
self
.
colourResolution
,
0
,
gl
.
GL_RGBA
,
gl
.
GL_UNSIGNED_BYTE
,
colourmap
)
def
configDisplayListeners
(
self
):
"""
Adds a bunch of listeners to the fslimage.ImageDisplay object which
defines how the given image is to be displayed. This is done so we
can update the colour texture when image display properties are
changed.
"""
def
colourUpdateNeeded
(
*
a
):
self
.
updateColourBuffer
()
display
=
self
.
display
lnrName
=
'
GlImageData_{}
'
.
format
(
id
(
self
))
display
.
addListener
(
'
displayMin
'
,
lnrName
,
colourUpdateNeeded
)
display
.
addListener
(
'
displayMax
'
,
lnrName
,
colourUpdateNeeded
)
display
.
addListener
(
'
rangeClip
'
,
lnrName
,
colourUpdateNeeded
)
display
.
addListener
(
'
cmap
'
,
lnrName
,
colourUpdateNeeded
)
This diff is collapsed.
Click to expand it.
fsl/fslview/slicecanvas.py
+
20
−
286
View file @
4759dd32
...
@@ -6,298 +6,21 @@
...
@@ -6,298 +6,21 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
#
import
numpy
as
np
import
numpy
as
np
import
wx
import
wx
import
wx.glcanvas
as
wxgl
import
wx.glcanvas
as
wxgl
import
OpenGL.GL
as
gl
import
OpenGL.GL
as
gl
import
OpenGL.arrays.vbo
as
vbo
# Under OS X, I don't think I can request an OpenGL 3.2 core profile
# Under OS X, I don't think I can request an OpenGL 3.2 core profile
# using wx - I'm stuck with OpenGL 2.1 I'm using these ARB extensions
# using wx - I'm stuck with OpenGL 2.1 I'm using these ARB extensions
# for functionality which is standard in 3.2.
# for functionality which is standard in 3.2.
import
OpenGL.GL.ARB.instanced_arrays
as
arbia
import
OpenGL.GL.ARB.instanced_arrays
as
arbia
import
OpenGL.GL.ARB.draw_instanced
as
arbdi
import
OpenGL.GL.ARB.draw_instanced
as
arbdi
import
OpenGL.GL.ARB.texture_rg
as
arbrg
import
fsl.data.fslimage
as
fslimage
class
GLImageData
(
object
):
"""
A GLImageData object encapsulates the OpenGL information necessary
to render 2D slices of a 3D image.
A slice from one image is rendered using four buffers and two textures.
The first buffer, the
'
geometry buffer
'
simply contains the 3D
coordinates (single precision floating point) of four vertices, which
define the geometry of a single voxel (using triangle strips).
The remaining buffers contain the X, Y, and Z coordinates of the voxels
in the slice to be displayed. These coordinates are stored as single
precision floating points, and used both to position a voxel, and to
look up its value in the 3D data texture (see below).
The image data itself is stored as a 3D texture, with each voxel value
stored as a single unsigned byte in the range 0-255.
Finally, a 1D texture is used is used to store a lookup table containing
an RGBA8 colour map, to colour each voxel according to its value.
"""
def
__init__
(
self
,
image
,
canvas
):
"""
Initialise the OpenGL data buffers required to render the given image.
Parameters:
- image: A fsl.data.fslimage.Image object.
- canvas: The SliceCanvas object which is rendering the image.
"""
self
.
image
=
image
self
.
canvas
=
canvas
# Maximum number of colours used to draw image data.
# Keep this to a power of two, as some GL implementations
# will complain/misbehave if it isn't.
self
.
colourResolution
=
256
self
.
initGLImageData
()
def
initGLImageData
(
self
):
"""
Creates and initialises the OpenGL data for the fslimage.Image
object that was passed to the GLImageData constructor.
"""
image
=
self
.
image
canvas
=
self
.
canvas
# The geometry buffer defines the geometry of
# a single voxel, rendered as a triangle strip.
geomData
=
np
.
zeros
((
4
,
3
),
dtype
=
np
.
float32
)
geomData
[:,
[
canvas
.
xax
,
canvas
.
yax
]]
=
[[
-
0.5
,
-
0.5
],
[
0.5
,
-
0.5
],
[
-
0.5
,
0.5
],
[
0.5
,
0.5
]]
geomData
=
geomData
.
ravel
(
'
C
'
)
geomBuffer
=
vbo
.
VBO
(
geomData
,
gl
.
GL_STATIC_DRAW
)
# x/y/z coordinates are stored as VBO arrays
voxData
=
[]
for
dim
in
image
.
shape
:
data
=
np
.
arange
(
0
,
dim
,
dtype
=
np
.
float32
)
voxData
.
append
(
data
)
# the screen x coordinate data has to be repeated (ydim)
# times - we are drawing row-wise, and opengl does not
# allow us to loop over a VBO in a single instance
# rendering call
voxData
[
canvas
.
xax
]
=
np
.
tile
(
voxData
[
canvas
.
xax
],
image
.
shape
[
canvas
.
yax
])
xBuffer
=
vbo
.
VBO
(
voxData
[
0
],
gl
.
GL_STATIC_DRAW
)
yBuffer
=
vbo
.
VBO
(
voxData
[
1
],
gl
.
GL_STATIC_DRAW
)
zBuffer
=
vbo
.
VBO
(
voxData
[
2
],
gl
.
GL_STATIC_DRAW
)
# The colour buffer, containing a map of
# colours (stored on the GPU as a 1D texture)
# This is initialised in the updateColourBuffer
# method
colourBuffer
=
gl
.
glGenTextures
(
1
)
self
.
dataBuffer
=
self
.
initImageBuffer
()
self
.
voxXBuffer
=
xBuffer
self
.
voxYBuffer
=
yBuffer
self
.
voxZBuffer
=
zBuffer
self
.
geomBuffer
=
geomBuffer
self
.
colourBuffer
=
colourBuffer
# Add listeners to this image so the view can be
# updated when its display properties are changed
self
.
configDisplayListeners
()
# Create the colour buffer for the given image
self
.
updateColourBuffer
()
def
initImageBuffer
(
self
):
"""
Initialises the OpenGL buffer used to store the data for the given
image. The buffer is stored as an attribute of the image and, if it
has already been created (e.g. by another SliceCanvas object), the
existing buffer is returned.
"""
image
=
self
.
image
texShape
=
2
**
(
np
.
ceil
(
np
.
log2
(
image
.
shape
)))
pad
=
[(
0
,
l
-
s
)
for
(
l
,
s
)
in
zip
(
texShape
,
image
.
shape
)]
self
.
imageTexShape
=
texShape
try
:
imageBuffer
=
image
.
getAttribute
(
'
glBuffers
'
)
except
:
imageBuffer
=
None
if
imageBuffer
is
not
None
:
return
imageBuffer
# The image data is normalised to lie
# between 0 and 255, and cast to uint8
imageData
=
np
.
array
(
image
.
data
,
dtype
=
np
.
float32
)
imageData
=
255.0
*
(
imageData
-
imageData
.
min
())
/
\
(
imageData
.
max
()
-
imageData
.
min
())
# and each dimension is padded so it has a
# power-of-two length. Ugh. This is a horrible,
# but as far as I'm aware necessary hack. At
# least it's necessary using the OpenGL 2.1
# API on OSX mavericks. It massively increases
# image load time, too.
imageData
=
np
.
pad
(
imageData
,
pad
,
'
constant
'
,
constant_values
=
0
)
imageData
=
np
.
array
(
imageData
,
dtype
=
np
.
uint8
)
# Then flattened, with fortran dimension ordering,
# so the data, as stored on the GPU, has its first
# dimension as the fastest changing.
imageData
=
imageData
.
ravel
(
order
=
'
F
'
)
# Image data is stored on the GPU as a 3D texture
imageBuffer
=
gl
.
glGenTextures
(
1
)
gl
.
glBindTexture
(
gl
.
GL_TEXTURE_3D
,
imageBuffer
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_MAG_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_MIN_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_S
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_T
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_3D
,
gl
.
GL_TEXTURE_WRAP_R
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexImage3D
(
gl
.
GL_TEXTURE_3D
,
0
,
arbrg
.
GL_R8
,
texShape
[
0
],
texShape
[
1
],
texShape
[
2
],
0
,
gl
.
GL_RED
,
gl
.
GL_UNSIGNED_BYTE
,
imageData
)
# And added as an attribute of the image, so
# other things which want to render the image
# don't need to recreate all of those buffers.
image
.
setAttribute
(
'
glBuffers
'
,
imageBuffer
)
return
imageBuffer
def
updateColourBuffer
(
self
):
"""
Regenerates the colour buffer used to colour image voxels.
"""
display
=
self
.
image
.
display
colourBuffer
=
self
.
colourBuffer
# Here we are creating a range of values to be passed
# to the matplotlib.colors.Colormap instance of the
# image display. We scale this range such that data
# values which lie outside the configured display range
# will map to values below 0.0 or above 1.0. It is
# assumed that the Colormap instance is configured to
# generate appropriate colours for these out-of-range
# values.
normalRange
=
np
.
linspace
(
0.0
,
1.0
,
self
.
colourResolution
)
normalStep
=
1.0
/
(
self
.
colourResolution
-
1
)
normMin
=
(
display
.
displayMin
-
display
.
dataMin
)
/
\
(
display
.
dataMax
-
display
.
dataMin
)
normMax
=
(
display
.
displayMax
-
display
.
dataMin
)
/
\
(
display
.
dataMax
-
display
.
dataMin
)
newStep
=
normalStep
/
(
normMax
-
normMin
)
newRange
=
(
normalRange
-
normMin
)
*
(
newStep
/
normalStep
)
# Create [self.colourResolution] rgb values,
# spanning the entire range of the image
# colour map
colourmap
=
display
.
cmap
(
newRange
)
# The colour data is stored on
# the GPU as 8 bit rgba tuples
colourmap
=
np
.
floor
(
colourmap
*
255
)
colourmap
=
np
.
array
(
colourmap
,
dtype
=
np
.
uint8
)
colourmap
=
colourmap
.
ravel
(
order
=
'
C
'
)
# GL texture creation stuff
gl
.
glBindTexture
(
gl
.
GL_TEXTURE_1D
,
colourBuffer
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_MAG_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_MIN_FILTER
,
gl
.
GL_NEAREST
)
gl
.
glTexParameteri
(
gl
.
GL_TEXTURE_1D
,
gl
.
GL_TEXTURE_WRAP_S
,
gl
.
GL_CLAMP_TO_EDGE
)
gl
.
glTexImage1D
(
gl
.
GL_TEXTURE_1D
,
0
,
gl
.
GL_RGBA8
,
self
.
colourResolution
,
0
,
gl
.
GL_RGBA
,
gl
.
GL_UNSIGNED_BYTE
,
colourmap
)
def
configDisplayListeners
(
self
):
"""
Adds a bunch of listeners to the fslimage.ImageDisplay object
(accessible as an attribute, called
'
display
'
, of the given image),
which defines how the given image is to be displayed. This is done
so we can refresh the image view when image display properties are
changed.
"""
def
refreshNeeded
(
*
a
):
"""
The view just needs to be refreshed (e.g. the alpha property
has changed).
"""
self
.
canvas
.
Refresh
()
def
colourUpdateNeeded
(
*
a
):
"""
The colour map for this image needs to be recreated (e.g. the
colour map has been changed).
"""
self
.
updateColourBuffer
()
self
.
canvas
.
Refresh
()
display
=
self
.
image
.
display
lnrName
=
'
SliceCanvas_{{}}_{}
'
.
format
(
id
(
self
))
refreshProps
=
[
'
alpha
'
,
'
enabled
'
]
colourUpdateProps
=
[
'
displayMin
'
,
'
displayMax
'
,
'
rangeClip
'
,
'
cmap
'
]
for
prop
in
refreshProps
:
display
.
addListener
(
prop
,
lnrName
.
format
(
prop
),
refreshNeeded
)
for
prop
in
colourUpdateProps
:
display
.
addListener
(
prop
,
lnrName
.
format
(
prop
),
colourUpdateNeeded
)
import
fsl.data.fslimage
as
fslimage
import
fsl.fslview.glimagedata
as
glimagedata
# The vertex shader positions and colours a single vertex.
# The vertex shader positions and colours a single vertex.
...
@@ -577,15 +300,26 @@ class SliceCanvas(wxgl.GLCanvas):
...
@@ -577,15 +300,26 @@ class SliceCanvas(wxgl.GLCanvas):
self
.
ypos
=
self
.
ypos
self
.
ypos
=
self
.
ypos
self
.
zpos
=
self
.
zpos
self
.
zpos
=
self
.
zpos
# Create a GLImageData object
# Create a GLImageData object for any new images,
# for any new images
# and attach a listener to their display properties
# so we know when to refresh the canvas.
for
image
in
self
.
imageList
:
for
image
in
self
.
imageList
:
try
:
try
:
glData
=
image
.
getAttribute
(
self
.
name
)
glData
=
image
.
getAttribute
(
self
.
name
)
except
:
except
:
glData
=
GLImageData
(
image
,
self
)
glData
=
glimagedata
.
GLImageData
(
image
,
self
.
xax
,
self
.
yax
)
image
.
setAttribute
(
self
.
name
,
glData
)
image
.
setAttribute
(
self
.
name
,
glData
)
def
refresh
(
*
a
):
self
.
Refresh
()
image
.
display
.
addListener
(
'
enabled
'
,
self
.
name
,
refresh
)
image
.
display
.
addListener
(
'
alpha
'
,
self
.
name
,
refresh
)
image
.
display
.
addListener
(
'
displayMin
'
,
self
.
name
,
refresh
)
image
.
display
.
addListener
(
'
displayMax
'
,
self
.
name
,
refresh
)
image
.
display
.
addListener
(
'
rangeClip
'
,
self
.
name
,
refresh
)
image
.
display
.
addListener
(
'
cmap
'
,
self
.
name
,
refresh
)
self
.
Refresh
()
self
.
Refresh
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment