Skip to content
Snippets Groups Projects
Commit 1a54c6c2 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

ImageView supports multiple images. Pretty hacky at this stage, needs cleaning...

ImageView supports multiple images. Pretty hacky at this stage, needs cleaning up. Have gone back to storing image data as uint8s.
parent 249a3357
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python
#
# fslimage.py - Classes representing a 3D image, the display
# properties of a 3D image, and a collection of 3D images.
# fslimage.py - Classes for representing 3D images, display
# properties of 3D images, and collections of 3D images.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
......@@ -39,6 +39,7 @@ class Image(object):
# otherwise, we assume that it is a nibabel image
self.nibImage = image
self.data = image.get_data()
self.name = image.get_filename()
xdim,ydim,zdim = self.nibImage.get_shape()
xlen,ylen,zlen = self.nibImage.get_header().get_zooms()
......@@ -51,6 +52,10 @@ class Image(object):
self.ylen = ylen
self.zlen = zlen
# This attribute may be used to point to an OpenGL
# buffer which is to be shared between multiple users
# (e.g. two SliceCanvas instances which are displaying
# a different view of the same image)
self.glBuffer = None
......@@ -59,7 +64,6 @@ class ImageDisplay(props.HasProperties):
A class which describes how an image should be displayed.
"""
def updateColourMap(self, newVal):
"""
When a colour property changes, this method is called -
......@@ -76,6 +80,8 @@ class ImageDisplay(props.HasProperties):
cmap.set_under(cmap(0.0), alpha=1.0)
cmap.set_over( cmap(1.0), alpha=1.0)
# The display properties of an image
enabled = props.Boolean()
alpha = props.Double(minval=0.0, maxval=1.0, default=1.0)
displayMin = props.Double()
displayMax = props.Double()
......@@ -85,9 +91,14 @@ class ImageDisplay(props.HasProperties):
cmap = props.ColourMap(default=mplcm.Greys_r,
preNotifyFunc=updateColourMap)
_view = props.VGroup(('displayMin', 'displayMax', 'alpha', 'rangeClip', 'cmap'))
_view = props.VGroup(('enabled',
'displayMin',
'displayMax',
'alpha',
'rangeClip',
'cmap'))
_labels = {
'enabled' : 'Enabled',
'displayMin' : 'Min.',
'displayMax' : 'Max.',
'alpha' : 'Opacity',
......@@ -112,6 +123,9 @@ class ImageDisplay(props.HasProperties):
class ImageList(object):
"""
Class representing a collection of images to be displayed together.
"""
def __init__(self, images=None, displays=None):
......@@ -120,3 +134,4 @@ class ImageList(object):
self.images = images
self.displays = displays
#!/usr/bin/env python
#
# imgshow.py - A wx/OpenGL widget for displaying and interacting with a 3D
# image. Displays three canvases, each of which shows a slice of the image
# along each dimension.
# imgshow.py - A wx/OpenGL widget for displaying and interacting with a
# collection of 3D image. Displays three canvases, each of which shows
# a slice of the images along each dimension.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
......@@ -29,27 +29,21 @@ LocationEvent, EVT_LOCATION_EVENT = wxevent.NewEvent()
class ImageView(wx.Panel):
def __init__(self, parent, image, *args, **kwargs):
def __init__(self, parent, imageList, *args, **kwargs):
"""
Creates three SliceCanvas objects, each displaying a
different axis of the given 3D numpy image.
"""
if not isinstance(image, fslimage.Image):
image = fslimage.Image(image)
self.imageDisplay = fslimage.ImageDisplay(image)
imageList = fslimage.ImageList([image], [self.imageDisplay])
self.imageList = imageList
wx.Panel.__init__(self, parent, *args, **kwargs)
self.SetMinSize((300,100))
self.shape = image.data.shape
self.shape = imageList.images[0].data.shape
self.canvasPanel = wx.Panel(self)
self.canvasPanel = wx.Panel(self)
self.controlPanel = props.buildGUI(self, self.imageDisplay)
self.xcanvas = slicecanvas.SliceCanvas(
self.canvasPanel, imageList, zax=0)
self.ycanvas = slicecanvas.SliceCanvas(
......@@ -57,6 +51,13 @@ class ImageView(wx.Panel):
self.zcanvas = slicecanvas.SliceCanvas(
self.canvasPanel, imageList, zax=2, context=self.xcanvas.context)
self.controlPanel = wx.Notebook(self)
for i in range(len(imageList.images)):
controlPanel = props.buildGUI(self.controlPanel, self.imageList.displays[i])
self.controlPanel.AddPage(controlPanel, '{}'.format(i))
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
self.canvasSizer = wx.BoxSizer(wx.HORIZONTAL)
......@@ -170,28 +171,32 @@ class ImageView(wx.Panel):
class ImageFrame(wx.Frame):
"""
Convenience class for displaying an image in a standalone window.
Convenience class for displaying a collection of images in a standalone
window.
"""
def __init__(self, parent, image, title=None):
def __init__(self, parent, imageList, title=None):
wx.Frame.__init__(self, parent, title=title)
self.image = image
self.panel = ImageView(self, image)
self.imageList = imageList
self.panel = ImageView(self, imageList)
self.Layout()
if __name__ == '__main__':
if len(sys.argv) != 2:
print 'usage: imageview.py filename'
if len(sys.argv) < 2:
print 'usage: imageview.py filename [filename]'
sys.exit(1)
app = wx.App()
image = fslimage.Image(sys.argv[1])
app = wx.App()
images = map(fslimage.Image, sys.argv[1:])
displays = map(fslimage.ImageDisplay, images)
imageList = fslimage.ImageList(images, displays)
frame = ImageFrame(
None,
image,
imageList,
title=sys.argv[1])
frame.Show()
......
......@@ -44,7 +44,7 @@ class GLImageData(object):
slice).
The third buffer, the 'image buffer' contains the image data itself,
scaled to lie between 0.0 and 1.0. It is used to calculate voxel colours.
scaled to lie between 0 and 255. It is used to calculate voxel colours.
Finally, the texture, the 'colour buffer', is used to store a lookup table
containing colours.
......@@ -375,11 +375,13 @@ class SliceCanvas(wxgl.GLCanvas):
if image.glBuffer is not None:
return image.glBuffer
# The image data is cast to single precision floating
# point, and normalised to lie between 0.0 and 1.0
# The image data is normalised to lie
# between 0 and 256, and cast to uint8
imageData = np.array(image.data, dtype=np.float32)
imageData = (imageData - imageData.min()) / \
(imageData.max() - imageData.min())
imageData = 255.0*(imageData - imageData.min()) / \
(imageData.max() - imageData.min())
imageData = np.array(imageData, dtype=np.uint8)
# Then flattened, with fortran dimension ordering,
# so the data, as stored on the GPU, has its first
......@@ -558,10 +560,10 @@ class SliceCanvas(wxgl.GLCanvas):
gl.glVertexAttribPointer(
self.voxelValuePos,
1,
gl.GL_FLOAT,
gl.GL_FALSE,
imageStride*4,
imageBuffer + imageOffset*4)
gl.GL_UNSIGNED_BYTE,
gl.GL_TRUE,
imageStride,
imageBuffer + imageOffset)
gl.glEnableVertexAttribArray(self.voxelValuePos)
arbia.glVertexAttribDivisorARB(self.voxelValuePos, 1)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment