3

I have a simple viewer in PyQt4, and I'm having some strange behavior when converting from a numpy array (8-bit grayscale) to a QImage to display. It seems to me like something is going haywire when I try to construct a QImage from a transposed array, even though the memory order should have been updated (i.e. it's not just a view with different strides).

Below is a heavily cut-down version of the viewer:

import numpy as np
from PyQt4 import QtGui

class SimpleViewer(QtGui.QMainWindow):

    def __init__(self, data, parent=None):
        super(SimpleViewer, self).__init__(parent=parent)

        hgt, wid = data.shape

        dmin = data.min()
        dmax = data.max()
        bdata = ((data - dmin)/(dmax - dmin)*255).astype(np.uint8)

        img = QtGui.QImage(bdata, wid, hgt, QtGui.QImage.Format_Indexed8)

        self.scene = QtGui.QGraphicsScene(0, 0, wid, hgt)
        self.px = self.scene.addPixmap(QtGui.QPixmap.fromImage(img))

        self.view = QtGui.QGraphicsView(self.scene)
        self.setCentralWidget(self.view)


if __name__ == "__main__":

    app = QtGui.QApplication.instance()
    if app is None:
        app = QtGui.QApplication(['python'])

    xx, yy = np.meshgrid(np.arange(-100,100), np.arange(350))
    zz = xx * np.cos(yy/350.*6*np.pi)

    viewer = SimpleViewer(zz)
    viewer.show()
    app.exec_()

Now, as-is this produces something like this, which is fine:

vertical-is-okay

However, I need to view my real images with the first dimension as "x" and the second dimension as "y" so it needs to be transposed (plus a y-flip so the origin is lower-left, but that's outside the scope here).

If I change hgt, wid = data.shape to wid, hgt = data.shape and do nothing else (which is wrong), I get the following. It makes sense, because the QImage is just reading the memory.

bad-memory-order

However, if I also pass in bdata.T.copy() instead of bdata, I'm expecting that the array will be transposed and the memory re-ordered by the copy operation. But what I get is this:

problem

It's so close but off by just a hair for some reason. Also, I noticed that if the dimensions just happen to be integer multiples of each other (e.g. 200x100), then it comes out okay. What is going on? Have I just missed something really really stupid?

I can't tell if this is a PyQt issue or a numpy issue ... the transposed array plots fine with matplotlib, but matplotlib handles the striding so that may not mean anything.

I get the same behavior in Python 2.7 and Python 3.5. The PyQt4 version is 4.11.4, using Qt version 4.8.7. Numpy version is 1.10.1.

1 Answer 1

1

The issue stems from the creation of the QImage.

img = QtGui.QImage(bdata, wid, hgt, QtGui.QImage.Format_Indexed8)

The documentation for the signature of the constructor you are using says

... data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.

If you change the dimensions of your array so that it is 32-bit aligned (for example using xx, yy = np.meshgrid(np.arange(-128,128), np.arange(384))) then your code works correctly.

To correct this issue without needing a multiple of 32 bytes per line, you want to use a slightly different constructor for the QImage:

QImage(data, width, height, bytesPerLine, format)

In your case, this is simply:

img = QtGui.QImage(bdata, wid, hgt, wid, QtGui.QImage.Format_Indexed8)

since each pixel contains 1 byte of information.

Sign up to request clarification or add additional context in comments.

1 Comment

Whoa. For some reason I must have stopped reading at "data must be 32-bit aligned" and totally missed the "and each scanline must also be 32-bit aligned". A lesson in reading documentation carefully...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.