Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import numpy
- from OpenGL.GL import *
- import threading
- import wx
- import wx.glcanvas
- import numpy
- import OpenGL.GL as GL
- import threading
- ## Maps numpy datatypes to OpenGL datatypes
- dtypeToGlTypeMap = {
- numpy.uint8: GL.GL_UNSIGNED_BYTE,
- numpy.uint16: GL.GL_UNSIGNED_SHORT,
- numpy.int16: GL.GL_SHORT,
- numpy.float32: GL.GL_FLOAT,
- numpy.float64: GL.GL_FLOAT,
- numpy.int32: GL.GL_FLOAT,
- numpy.uint32: GL.GL_FLOAT,
- numpy.complex64: GL.GL_FLOAT,
- numpy.complex128: GL.GL_FLOAT,
- }
- ## Maps numpy datatypes to the maximum value the datatype can represent
- dtypeToMaxValMap = {
- numpy.uint16: (1 << 16) - 1,
- numpy.int16: (1 << 15) - 1,
- numpy.uint8: (1 << 8) - 1,
- numpy.bool_: (1 << 8) - 1,
- numpy.float32: 1
- }
- ## This class handles display of a single 2D array of pixel data.
- class Image:
- def __init__(self):
- self.imageData = None
- self.imageMin = None
- self.imageMax = None
- self.textureID = None
- self.color = (1, 1, 1)
- self.lock = threading.Lock()
- self.bindTexture()
- self.refresh()
- def bindTexture(self):
- if self.imageData is None:
- return
- self.lock.acquire()
- pic_ny, pic_nx = self.imageData.shape
- if self.imageMin == 0 and self.imageMax == 0:
- self.imageMin = self.imageData.min()
- self.imageMax = self.imageData.max()
- # 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
- if imgType not in dtypeToGlTypeMap:
- raise ValueError, "Unsupported data mode %s" % str(imgType)
- GL.glTexImage2D(GL.GL_TEXTURE_2D,0, GL.GL_RGB, tex_nx,tex_ny, 0,
- GL.GL_LUMINANCE, dtypeToGlTypeMap[imgType], None)
- self.lock.release()
- def refresh(self):
- if self.imageData is None:
- return
- self.bindTexture()
- minMaxRange = float(self.imageMax - self.imageMin)
- if abs(self.imageMax - self.imageMin) < 1:
- minMaxRange = 1
- imgType = self.imageData.dtype.type
- fBias = -self.imageMin / minMaxRange
- f = dtypeToMaxValMap[imgType] / 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
- if imgType not in dtypeToGlTypeMap:
- raise ValueError, "Unsupported data mode %s" % str(imgType)
- GL.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, pic_nx, pic_ny,
- GL.GL_LUMINANCE, dtypeToGlTypeMap[imgType], imgString)
- def render(self):
- if self.imageData is None or self.textureID is None:
- return
- self.refresh()
- GL.glPushMatrix()
- GL.glColor3fv(self.color)
- 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()
- GL.glPopMatrix()
- ## Free the allocated GL texture
- def wipe(self):
- self.lock.acquire()
- if self.textureID is not None:
- GL.glDeleteTextures(self.textureID)
- self.textureID = None
- self.lock.release()
- ## Accept a new array of image data.
- def updateImage(self, imageData, imageMin, imageMax):
- self.imageData = imageData
- self.imageMin = imageMin
- self.imageMax = imageMax
- self.wipe()
- self.refresh()
- colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
- ## Simple window that contains a GLViewer instance.
- class ViewerWindow(wx.Frame):
- def __init__(self, parent, **kwargs):
- title = "Multi-wavelength view"
- wx.Frame.__init__(self, parent, title = title,
- style = wx.DEFAULT_FRAME_STYLE)
- sizer = wx.BoxSizer(wx.VERTICAL)
- self.viewer = GLViewer(self, size = (512, 512), **kwargs)
- sizer.Add(self.viewer)
- self.SetSizerAndFit(sizer)
- ## This lock prevents us from trying to update more than one
- # viewer at a time, evading some OpenGL errors.
- self.lock = threading.Lock()
- self.Show()
- ## Display a new image in the viewer, or hide the viewer if it's
- # been disconnected.
- def processNewImage(self, camId):
- if not self.viewer.haveInitedGL:
- return
- self.lock.acquire()
- data = numpy.random.random_integers(0, 10, (512, 512)).astype(numpy.uint16)
- self.viewer.updateImage(camId, data, 0, 10)
- color = colors[camId]
- # Remap from [0, 255] to [0, 1]
- color = [c / 255.0 for c in color]
- self.viewer.setColor(camId, color, False)
- self.viewer.Refresh()
- self.lock.release()
- ## OpenGL canvas for displaying multiple camera views layered on top of each
- # other.
- class GLViewer(wx.glcanvas.GLCanvas):
- ## Instantiate.
- def __init__(self, parent,
- style = 0, size = wx.DefaultSize):
- wx.glcanvas.GLCanvas.__init__(self, parent, style = style, size = size)
- ## List of Image instances
- self.imgList = []
- for i in xrange(3):
- self.imgList.append(Image())
- ## Whether or not we've done some one-time initialization work.
- self.haveInitedGL = False
- wx.EVT_PAINT(self, self.OnPaint)
- # Do nothing on background erasure, to avoid flickering.
- wx.EVT_ERASE_BACKGROUND(self, lambda event: event)
- 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
- def updateImage(self, index, data, imageMin, imageMax):
- self.pic_ny, self.pic_nx = data.shape
- self.imgList[index].updateImage(data, imageMin, imageMax)
- def setColor(self, imgidx, color, RefreshNow=1):
- self.imgList[imgidx].color = color
- if RefreshNow:
- 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()
- glEnable(GL_TEXTURE_2D)
- glEnable(GL_BLEND)
- glBlendFunc(GL_ONE, GL_ONE)
- for image in self.imgList:
- if image.imageData is not None:
- image.render()
- glDisable(GL_TEXTURE_2D)
- glDisable(GL_BLEND)
- glFlush()
- glPopMatrix()
- self.SwapBuffers()
- def OnReload(self, event=None):
- self.Refresh(False)
- app = wx.App(False)
- window = ViewerWindow(None)
- import time
- def eventSpammer():
- while True:
- for i in xrange(3):
- event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, i)
- wx.PostEvent(window, event)
- time.sleep(.05)
- def onNewImageReady(event):
- window.processNewImage(event.GetId())
- # Listen to events
- for i in xrange(3):
- wx.EVT_BUTTON(window, i, onNewImageReady)
- print "Event listeners started"
- threading.Thread(target = eventSpammer).start()
- print "Spammer started"
- app.MainLoop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement