Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I want to create a PyOpenGL/QtOpenGL widget that will allow me to visualize an arbitrary NumPy 3D matrix, not unlike the following Hinton diagram envisioned as a "cube of cubes" instead of a "square of squares":

I'm having a bit of a rough time with OpenGL though. Here is my code thus far:

from OpenGL.GL import *
from OpenGL.GLUT import *
from PyQt4 import QtGui, QtOpenGL
import numpy as np

action_keymap = {
  # 'a': lambda: glTranslate(-1, 0, 0),
  # 'd': lambda: glTranslate( 1, 0, 0),
  # 'w': lambda: glTranslate( 0, 1, 0),
  # 's': lambda: glTranslate( 0,-1, 0),

  'a': lambda: glRotate(-5, 0, 1, 0),
  'd': lambda: glRotate( 5, 0, 1, 0),
  # 'W': lambda: glRotate(-5, 1, 0, 0),
  # 'S': lambda: glRotate( 5, 1, 0, 0),
}

ARRAY = np.ones([3,3,3])

class GLWidget(QtOpenGL.QGLWidget):

  def paintGL(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    for idx, value in np.ndenumerate(ARRAY):
      rel_pos = np.array(idx)/np.max(ARRAY.shape)
      glTranslate(* rel_pos)
      glutSolidCube(0.9/np.max(ARRAY.shape))
      glTranslate(*-rel_pos)

  def resizeGL(self, w, h):
    glLoadIdentity()
    glRotate(35,1,0,0)
    glRotate(45,0,1,0)

  def initializeGL(self):
    glClearColor(0.1, 0.1, 0.3, 1.0)

  def keyPressEvent(self, event):
    action = action_keymap.get(str(event.text()))
    if action:
        action()
    self.updateGL()

  def mousePressEvent(self, event):
    super().mousePressEvent(event)
    self.press_point = event.pos()

  def mouseMoveEvent(self, event):
    super().mouseMoveEvent(event)
    motion = event.pos()-self.press_point
    self.press_point = event.pos()
    glRotate(motion.x(),0,1,0)
    glRotate(motion.y(),1,0,0)
    self.updateGL()

if __name__ == '__main__':
  app = QtGui.QApplication(sys.argv)

  w = GLWidget()
  w.show()

  sys.exit(app.exec_())

My problems are as follows:

1) Lighting. I've been reading up on lighting and materials, but I cannot seem to get a simple light somewhere giving the shape some clarity. I'd like the simplest, most basic possible light to be able to distinguish the squares instead of them being all rendered as pure white on all sides. I know how to change the color, but it doesn't alleviate the problem. What is the simplest light I can shine on this lattice to get some clarity on the subcomponents?

2) It is slow. I'll work out the math to achieve proper positioning and resizing of squares down the line, but I was wondering if there was a way to vectorize the process (after all, it's only turning the index into a translation and the value into a cube size for every element in the array). Should I write an extension in cpp, wrap my code with ctypes, or is there a way to outsource the work to OpenGL explicitly? What is the standard way to send a repetitive task to OpenGL from Python?

share|improve this question
    
IMO it's going to be very difficult to get a visualization like this to work well. Unlike the 2d grid of 2d squares, a 3d grid of 3d cubes will always suffer from occlusions, both in visibility and in lighting. It might be easier to come up with a way of "pushing" your 3d data into a 2d format -- for instance, by creating a Hinton diagram that also somehow indicates min/max/mean/std for each cell, or the like. Sounds like a really interesting visualization problem though, good luck with it ! –  lmjohns3 Aug 6 '13 at 16:43
    
Yeah, I realize that particularly with large matrices whatever is on the inside will not be visible from the outside (am I interpreting the term "occlusion" wrong?). I was planning on allowing the user to "zoom in" and make the blocks outside invisible down the line. Right now all I want is to be able to tell at a glance how many cells are there in an arbitrary array. I would be happy with a wireframe cube with lines marking subdivisions. –  RodericDay Aug 6 '13 at 16:47
    
I'm not an OpenGL expert, so even though I've set up lighting before in some limited cases, I wouldn't be able to explain how to do it. One suggestion : replace glutSolidCube with glutWireCube ? –  lmjohns3 Aug 6 '13 at 16:49
    
glutWireCube works fine when there's only a few cubes, but when I go to 10x10x10 or higher it just looks like a super dense mesh. my best results so far involved following a Joaqim app I found on Google. I didn't do a very good job, though, since the top of all cubes was white and the bottom of all cubes was black, but it allowed some idea of what was what. –  RodericDay Aug 6 '13 at 16:52

1 Answer 1

This won't directly create the sort of visualization you're looking for, but I would highly recommend taking a look at the glumpy package by Nicholas Rougier : https://code.google.com/p/glumpy/. OpenGL can be a pain to use, especially for someone who is not a graphics expert, and glumpy abstracts away most of the pain to let you just display numpy arrays on the screen.

share|improve this answer
    
this looks very interesting. the line The dtype of array must be one of numpy.uint8 or numpy.float32 (because on existing restriction on OpenGL textures data types) and the shape of the array must be one of M, MxN or MxNx[1,2,3,4]. worries me a bit, however, since I want to work with MxNxK arrays. Then again, he does have a cool-looking cube in the landing page, so maybe it will work. I will look into it. –  RodericDay Aug 6 '13 at 16:48
    
Yeah. Even though we can see in 3D, we really only consume visualizations effectively in 2D. One way or another you're going to need to map your data onto some sort of 2D surface -- after all, eventually it's going to be displayed on a 2D screen ! One specific thought here would be to set up a glumpy.Image to show your 3D array, but only show one 2D slice of it at a time. Then you could attach a scroll-wheel or keyboard event to let the user change which slice is shown. In my experience something like this would be pretty fast, but it would show up as a colormap rather than cubes. –  lmjohns3 Aug 6 '13 at 17:03
    
thanks for helping me out. I'll shoot you a message when I resolve this issue either way. –  RodericDay Aug 6 '13 at 18:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.