Mailerdaimon
Mailerdaimon

Reputation: 6080

QImage to Numpy Array using PySide

I am currently switching from PyQt to PySide.

With PyQt I converted QImage to a Numpy.Array using this code that I found on SO:

def convertQImageToMat(incomingImage):
    '''  Converts a QImage into an opencv MAT format  '''

    incomingImage = incomingImage.convertToFormat(4)

    width = incomingImage.width()
    height = incomingImage.height()

    ptr = incomingImage.bits()
    ptr.setsize(incomingImage.byteCount())
    arr = np.array(ptr).reshape(height, width, 4)  #  Copies the data
    return arr

However ptr.setsize(incomingImage.byteCount()) does not work with PySide as this is part of the void* support of PyQt.

My Question is: How can I convert a QImage to a Numpy.Array using PySide.

EDIT:

Version Info
> Windows 7 (64Bit)
> Python 2.7
> PySide Version 1.2.1
> Qt Version 4.8.5

Upvotes: 12

Views: 12578

Answers (4)

trumpetlicks
trumpetlicks

Reputation: 7065

I have to admit, all of the previous answers fail horribly when dealing with images that don't necessarily have an alpha, much less an even amount of channels (say a compressed grayscale that only stores the single channel). I just had to invent this workaround for myself, and this code works (I'm pretty sure for any image).

def QImageToCvMat(incomingImage):
    '''  Converts a QImage into an opencv/numpy MAT format  '''

    
    ba = QByteArray()
    buff = QBuffer(ba)

    # Essentially open up a "RAM" file
    buff.open(QIODevice.ReadWrite)

    # Store a PNG formatted file into the "RAM" File
    incomingImage.save(buff, "PNG")

    # Convert the now PNG contents into a numpy array of bytes
    fBytes = np.asarray(bytearray(ba.data())), dtype=np.uint8)

    # Let OpenCV "decode" the bytes in RAM as a PNG
    return cv2.imdecode(fBytes, cv.IMREAD_COLOR)

Upvotes: 2

JTIM
JTIM

Reputation: 2771

For me the solution with constBits() did not work, but the following worked:

def QImageToCvMat(incomingImage):
    '''  Converts a QImage into an opencv MAT format  '''

    incomingImage = incomingImage.convertToFormat(QtGui.QImage.Format.Format_RGBA8888)

    width = incomingImage.width()
    height = incomingImage.height()

    ptr = incomingImage.bits()
    ptr.setsize(height * width * 4)
    arr = np.frombuffer(ptr, np.uint8).reshape((height, width, 4))
    return arr

Upvotes: 7

Mailerdaimon
Mailerdaimon

Reputation: 6080

The trick is to use QImage.constBits() as suggested by @Henry Gomersall. The code I use now is:

def QImageToCvMat(self,incomingImage):
    '''  Converts a QImage into an opencv MAT format  '''

    incomingImage = incomingImage.convertToFormat(QtGui.QImage.Format.Format_RGB32)

    width = incomingImage.width()
    height = incomingImage.height()

    ptr = incomingImage.constBits()
    arr = np.array(ptr).reshape(height, width, 4)  #  Copies the data
    return arr

Upvotes: 3

Henry Gomersall
Henry Gomersall

Reputation: 8692

PySide doesn't seem to offer a bits method. How about using constBits to get the pointer to the array?

Upvotes: 3

Related Questions