import wx
import wx.glcanvas
import numpy
def makeWindow():
global window
window = ViewerWindow(None, tileShape = (3, 3), tileSize = 512)
# Generate a test image to display -- a sequence of squares.
width = 2056
height = 2548
data = numpy.zeros((width, height), dtype = numpy.uint16)
base = 0
for i in xrange(0, width, 512):
for j in xrange(0, height, 512):
data[i:i+512, j:j+512] = base
base += 50
# Downsample to make the image reasonably displayable on a normal monitor.
window.viewer.setImage(data[::2, ::2])
class App(wx.App):
def OnInit(self):
makeWindow()
return True
from OpenGL.GL import *
## Simple window that contains a GLViewer instance.
class ViewerWindow(wx.Frame):
def __init__(self, parent, tileShape, tileSize, *args, **kwargs):
wx.Frame.__init__(self, parent, *args, **kwargs)
self.viewer = GLViewer(self, tileShape, tileSize, **kwargs)
self.Show()
self.Bind(wx.EVT_SIZE, self.onSize)
def onSize(self, event):
self.viewer.setSize(list(self.GetSize()))
event.Skip()
class GLViewer(wx.glcanvas.GLCanvas):
## Instantiate.
def __init__(self, parent, tileShape, tileSize, *args, **kwargs):
wx.glcanvas.GLCanvas.__init__(self, parent, *args, **kwargs)
## Edge length of one tile.
self.tileSize = tileSize
## Shape of our tile grid.
self.tileShape = tileShape
## 2D array of Image instances. We'll create these when we get
# some image data to work with.
self.tiles = []
## Whether or not we've done some one-time initialization work.
self.haveInitedGL = False
self.scaleX = self.scaleY = 1.0
wx.EVT_PAINT(self, self.OnPaint)
def InitGL(self):
self.w, self.h = self.GetClientSizeTuple()
self.SetCurrent()
glClearColor(0.3, 0.3, 0.3, 0.0) ## background color
self.haveInitedGL = True
## Receive a new image and parcel it up into our smaller Image instances.
# Create those instances at this time if we don't have them already.
def setImage(self, newImage):
self.SetCurrent()
print "Updating new image to one of shape",newImage.shape
# Ensure that we have the right amount of data to put into our
# Images, by filling in any missing data with zeros. There's an
# implicit assumption here that newImage is smaller than or
# equal in size to our tiles' total combined area.
width = self.tileShape[0] * self.tileSize
height = self.tileShape[1] * self.tileSize
paddedImage = numpy.zeros((width, height), dtype = newImage.dtype)
paddedImage[:newImage.shape[0], :newImage.shape[1]] = newImage
print "Made padded image of shape",paddedImage.shape
for i in xrange(self.tileShape[0]):
self.tiles.append([])
for j in xrange(self.tileShape[1]):
subData = paddedImage[
i * self.tileSize : (i + 1) * self.tileSize,
j * self.tileSize : (j + 1) * self.tileSize]
self.tiles[i].append(Image(subData))
wx.CallAfter(self.changeHistScale, newImage.min(), newImage.max())
self.Refresh()
## Rescale our images so smin displays as black and smax as white.
def changeHistScale(self, smin ,smax):
for i in xrange(self.tileShape[0]):
for j in xrange(self.tileShape[1]):
self.tiles[i][j].refresh(smin, smax)
self.Refresh(False)
def setSize(self, size):
xSize = float(self.tileShape[0] * self.tileSize)
ySize = float(self.tileShape[1] * self.tileSize)
self.scaleX = size[0] / xSize
self.scaleY = size[1] / ySize
self.w, self.h = size
self.Refresh(0)
def OnPaint(self, event):
try:
dc = wx.PaintDC(self)
except:
return
if not self.haveInitedGL:
self.InitGL()
self.SetCurrent()
glViewport(0, 0, self.w, self.h)
glMatrixMode (GL_PROJECTION)
glLoadIdentity ()
glOrtho (0, self.w, 0, self.h, 1., -1.)
glMatrixMode (GL_MODELVIEW)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glLoadIdentity()
glScalef(self.scaleX, self.scaleY, 1)
glEnable(GL_TEXTURE_2D)
for i in xrange(self.tileShape[0]):
for j in xrange(self.tileShape[1]):
glPushMatrix()
glTranslatef(i * self.tileSize, j * self.tileSize, 0)
self.tiles[i][j].render()
glPopMatrix()
glDisable(GL_TEXTURE_2D)
glPopMatrix()
glFlush()
self.SwapBuffers()
import OpenGL.GL as GL
## This class handles display of a single 2D array of pixel data.
class Image:
def __init__(self, imageData):
self.imageData = imageData
self.bindTexture()
def bindTexture(self):
pic_ny, pic_nx = self.imageData.shape
# Generate texture sizes that are powers of 2
tex_nx = 2
while tex_nx < pic_nx:
tex_nx *= 2
tex_ny = 2
while tex_ny < pic_ny:
tex_ny *= 2
self.picTexRatio_x = float(pic_nx) / tex_nx
self.picTexRatio_y = float(pic_ny) / tex_ny
self.textureID = GL.glGenTextures(1)
GL.glBindTexture(GL.GL_TEXTURE_2D, self.textureID)
# Define this new texture object based on self.imageData's geometry
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
GL.GL_NEAREST)
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
GL.GL_NEAREST)
imgType = self.imageData.dtype.type
GL.glTexImage2D(GL.GL_TEXTURE_2D,0, GL.GL_RGB, tex_nx,tex_ny, 0,
GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT, None)
# Re-load our image data onto the card, e.g. because our blackpoint and
# whitepoint have changed.
def refresh(self, imageMin, imageMax):
minMaxRange = float(imageMax - imageMin)
if abs(imageMax - imageMin) < 1:
minMaxRange = 1
imgType = self.imageData.dtype.type
fBias = -imageMin / minMaxRange
f = ((1 << 16) - 1) / minMaxRange
GL.glBindTexture(GL.GL_TEXTURE_2D, self.textureID)
GL.glPixelTransferf(GL.GL_RED_SCALE, f)
GL.glPixelTransferf(GL.GL_GREEN_SCALE, f)
GL.glPixelTransferf(GL.GL_BLUE_SCALE, f)
GL.glPixelTransferf(GL.GL_RED_BIAS, fBias)
GL.glPixelTransferf(GL.GL_GREEN_BIAS, fBias)
GL.glPixelTransferf(GL.GL_BLUE_BIAS, fBias)
GL.glPixelTransferf(GL.GL_MAP_COLOR, False)
GL.glPixelStorei(GL.GL_UNPACK_SWAP_BYTES,
not self.imageData.dtype.isnative)
GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, self.imageData.itemsize)
imgString = self.imageData.tostring()
pic_ny, pic_nx = self.imageData.shape
GL.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, pic_nx, pic_ny,
GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT, imgString)
def render(self):
cx,cy = self.imageData.shape[-1]/2., self.imageData.shape[-2]/2.
GL.glBindTexture(GL.GL_TEXTURE_2D, self.textureID)
GL.glBegin(GL.GL_QUADS)
pic_ny, pic_nx = self.imageData.shape
###//(0,0) at left bottom
GL.glTexCoord2f(0, 0)
GL.glVertex2i(0, 0)
GL.glTexCoord2f(self.picTexRatio_x, 0)
GL.glVertex2i(pic_nx, 0)
GL.glTexCoord2f(self.picTexRatio_x, self.picTexRatio_y)
GL.glVertex2i(pic_nx, pic_ny)
GL.glTexCoord2f(0, self.picTexRatio_y)
GL.glVertex2i(0, pic_ny)
GL.glEnd()
app = App(redirect = False)
app.MainLoop()